<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>栈外之地</title>
    <link>https://log1992.cn/</link>
    <description>代码之外，仍有值得记录的世界</description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, Logan</copyright>
    <lastBuildDate>Wed, 19 Aug 2026 15:05:00 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://log1992.cn/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Kratos主题微调：默认深色与全站圆角</title>
      <link>https://log1992.cn/2026/08/19/play/Kratos%E4%B8%BB%E9%A2%98%E5%BE%AE%E8%B0%83%EF%BC%9A%E9%BB%98%E8%AE%A4%E6%B7%B1%E8%89%B2%E4%B8%8E%E5%85%A8%E7%AB%99%E5%9C%86%E8%A7%92/</link>
      <description>
        <![CDATA[<blockquote>
<p>Kratos-Rebirth 用了挺久，一直有两个不顺眼的地方：没有「默认深色」的开关，全站还是一刀切的直角。这篇记录不用改主题源码、纯靠配置注入完成这两项微调的过程，附全部代码。</p>
</blockquote>
<h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>先说结论性]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/Hexo/">Hexo</category>
      <category domain="https://log1992.cn/tags/Kratos/">Kratos</category>
      <category domain="https://log1992.cn/tags/CSS/">CSS</category>
      <pubDate>Wed, 19 Aug 2026 15:05:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>Kratos-Rebirth 用了挺久，一直有两个不顺眼的地方：没有「默认深色」的开关，全站还是一刀切的直角。这篇记录不用改主题源码、纯靠配置注入完成这两项微调的过程，附全部代码。</p></blockquote><h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>先说结论性认知：<strong>Kratos-Rebirth 的明暗模式是「跟随系统 + localStorage 记忆」</strong>。</p><p>翻一下它的 <code>kr-theme.min.js</code> 就明白了：首次访问时看你系统的 <code>prefers-color-scheme</code>，你手动点过切换按钮后就用 <code>localStorage[&#39;user-color-scheme&#39;]</code> 记住你的选择。设计是合理的，但问题是——我就想默认深色，系统偏偏是浅色，主题又没提供这个配置项。</p><p>圆角是另一个执念。主题作者走的是硬朗直角风，文章卡片、侧栏挂件、弹窗、按钮全部 90 度棱角分明。风格见仁见智，但配合我现在这套皮肤，圆一点确实顺眼多了。</p><p>改 <code>node_modules</code> 里的主题源码？想都别想，<strong>一次 <code>npm install</code> 就打回原形</strong>。好在 Kratos-Rebirth 提供了 <code>additional_injections</code> 配置注入点，可以在 <code>_config.kratos-rebirth.yml</code> 里往页面 <code>&lt;head&gt;</code> 塞自定义的脚本和样式，主题升级也不受影响。</p><h4 id="默认深色：抢在主题脚本前面写-localStorage"><a href="#默认深色：抢在主题脚本前面写-localStorage" class="headerlink" title="默认深色：抢在主题脚本前面写 localStorage"></a>默认深色：抢在主题脚本前面写 localStorage</h4><p>思路很简单：既然主题启动时会读 <code>localStorage</code>，那我在它之前把值预设成 <code>dark</code> 不就行了。</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">additional_injections:</span></span><br><span class="line">  <span class="attr">head:</span> <span class="string">|</span></span><br><span class="line"><span class="string">    &lt;script&gt;</span></span><br><span class="line"><span class="string">    /* 首次访问默认深色主题（用户手动切换后尊重其选择） */</span></span><br><span class="line"><span class="string">    (function () &#123;</span></span><br><span class="line"><span class="string">      try &#123;</span></span><br><span class="line"><span class="string">        var KEY = &#x27;user-color-scheme&#x27;;</span></span><br><span class="line"><span class="string">        if (!localStorage.getItem(KEY)) localStorage.setItem(KEY, &#x27;dark&#x27;);</span></span><br><span class="line"><span class="string">        if (localStorage.getItem(KEY) === &#x27;dark&#x27;) &#123;</span></span><br><span class="line"><span class="string">          var el = document.getElementById(&#x27;theme-dark-css&#x27;);</span></span><br><span class="line"><span class="string">          if (el) &#123; el.setAttribute(&#x27;media&#x27;, &#x27;all&#x27;); el.removeAttribute(&#x27;disabled&#x27;); &#125;</span></span><br><span class="line"><span class="string">          document.documentElement.setAttribute(&#x27;data-theme&#x27;, &#x27;dark&#x27;);</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125; catch (e) &#123; /* 隐私模式下静默降级 */ &#125;</span></span><br><span class="line"><span class="string">    &#125;)();</span></span><br><span class="line"><span class="string">    &lt;/script&gt;</span></span><br></pre></td></tr></table></figure><p>几个细节：</p><ul><li><strong>只写一次</strong>：<code>if (!localStorage.getItem(KEY))</code> 保证只对没做过选择的访客生效，手动切回浅色的用户不会被强行掰回来</li><li><strong>光写 localStorage 不够</strong>：主题的深色样式表初始是 <code>media=&quot;(prefers-color-scheme: dark)&quot;</code>，要立刻把它的 <code>media</code> 改成 <code>all</code> 并设置 <code>data-theme</code>，不然从浅色跳深色会闪一下白屏</li><li><strong>try-catch 兜底</strong>：Safari 隐私模式下 <code>localStorage</code> 直接抛异常，别让整个脚本挂掉</li></ul><h4 id="全站圆角：摸类名-border-radius-轰炸"><a href="#全站圆角：摸类名-border-radius-轰炸" class="headerlink" title="全站圆角：摸类名 + border-radius 轰炸"></a>全站圆角：摸类名 + border-radius 轰炸</h4><p>Kratos 的样式是编译后的 <code>kr-core.min.css</code>，没有源码可改（也不想改），所以直接在注入的 <code>&lt;style&gt;</code> 里用选择器覆盖。先把主要元素的类名摸出来：</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="string">&lt;style&gt;</span></span><br><span class="line"><span class="string">/*</span> <span class="string">全站圆角</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-entry-border</span> &#123; <span class="attr">border-radius:</span> <span class="string">14px</span> <span class="type">!important;</span> &#125;          <span class="string">/*</span> <span class="string">文章卡片</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-hentry</span> &#123; <span class="attr">border-radius:</span> <span class="string">14px</span> <span class="type">!important;</span> &#125;</span><br><span class="line"><span class="string">.kratos-entry-post-meta</span> &#123; <span class="attr">border-radius:</span> <span class="number">0</span> <span class="number">0</span> <span class="string">14px</span> <span class="string">14px</span> <span class="type">!important;</span> &#125; <span class="string">/*</span> <span class="string">卡片底栏</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-entry-thumb-img</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px</span> <span class="type">!important;</span> &#125;       <span class="string">/*</span> <span class="string">缩略图</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.widget</span> &#123; <span class="attr">border-radius:</span> <span class="string">14px</span> <span class="type">!important;</span> <span class="attr">overflow:</span> <span class="string">hidden;</span> &#125;     <span class="string">/*</span> <span class="string">侧栏挂件</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.widget-kratos-about</span> <span class="string">.photo-background</span> &#123; <span class="attr">border-radius:</span> <span class="string">14px</span> <span class="string">14px</span> <span class="number">0</span> <span class="number">0</span> <span class="type">!important;</span> &#125; <span class="string">/*</span> <span class="string">个人信息卡顶部</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-copyright</span> &#123; <span class="attr">border-radius:</span> <span class="string">14px</span> <span class="type">!important;</span> &#125;</span><br><span class="line"><span class="string">.page-number</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px</span> <span class="type">!important;</span> &#125;                  <span class="string">/*</span> <span class="string">分页</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.navigation.post-navigation</span> <span class="string">.nav-previous</span> <span class="string">a,</span></span><br><span class="line"><span class="string">.navigation.post-navigation</span> <span class="string">.nav-next</span> <span class="string">a</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px</span> <span class="type">!important;</span> &#125; <span class="string">/*</span> <span class="string">上一篇下一篇</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.post-actions</span> <span class="string">.donate,</span></span><br><span class="line"><span class="string">.post-actions</span> <span class="string">.share</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px</span> <span class="type">!important;</span> &#125;          <span class="string">/*</span> <span class="string">打赏分享按钮</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kr-modal-content</span> &#123; <span class="attr">border-radius:</span> <span class="string">16px</span> <span class="type">!important;</span> <span class="attr">overflow:</span> <span class="string">hidden;</span> &#125; <span class="string">/*</span> <span class="string">弹窗</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kr-modal-header</span> &#123; <span class="attr">border-radius:</span> <span class="string">16px</span> <span class="string">16px</span> <span class="number">0</span> <span class="number">0</span> <span class="type">!important;</span> &#125;</span><br><span class="line"><span class="string">.kr-tool</span> <span class="string">.box</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px</span> <span class="type">!important;</span> &#125;                 <span class="string">/*</span> <span class="string">右下角浮动按钮</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-post-content</span> <span class="string">img</span> &#123; <span class="attr">border-radius:</span> <span class="string">8px;</span> &#125;                  <span class="string">/*</span> <span class="string">正文元素</span> <span class="string">*/</span></span><br><span class="line"><span class="string">.kratos-post-content</span> <span class="string">pre</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px;</span> &#125;</span><br><span class="line"><span class="string">.kratos-post-content</span> <span class="string">table</span> &#123; <span class="attr">border-radius:</span> <span class="string">10px;</span> <span class="attr">overflow:</span> <span class="string">hidden;</span> &#125;</span><br><span class="line"><span class="string">&lt;/style&gt;</span></span><br></pre></td></tr></table></figure><p><code>!important</code> 是必须的——编译后的样式特指性不低，注入样式又排在它后面加载，加上 <code>!important</code> 最省心。</p><h4 id="踩坑小结"><a href="#踩坑小结" class="headerlink" title="踩坑小结"></a>踩坑小结</h4><ul><li><strong>「改了没生效」九成是浏览器缓存</strong>。中途有一轮怎么改都没反应，最后发现浏览器缓存了旧的 <code>&lt;style&gt;</code>，强制刷新（Cmd+Shift+R）世界立刻美好。调样式先怀疑缓存，能少走一半弯路</li><li><strong><code>overflow: hidden</code> 是圆角的好搭档</strong>。挂件和弹窗内部有带背景色的头部块（比如个人信息卡的顶部背景图、弹窗的彩色标题条），光给外层容器加圆角没用，内部色块会用直角顶出来。容器加 <code>overflow: hidden</code> 一刀剪平</li><li><strong>圆角要一轮一轮补</strong>。第一版只覆盖了肉眼最大的卡片，结果卡片底栏、个人信息卡顶部、上一篇下一篇、打赏弹窗、右下角浮动按钮全是漏网之鱼——<strong>直角这东西，漏一处就特别显眼</strong>。终极方案就是把页面从上到下扫一遍，<code>class</code> 名一个个从渲染后的 HTML 里扒</li><li><strong><code>hexo server</code> 改配置后要重启</strong>。watch 模式对模板变更是热更新，但 <code>_config</code> 变更后它经常拿旧缓存糊弄你，<code>clean + 重启 server</code> 才是稳妥操作</li></ul><h4 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h4><p>整套改动就一个配置文件 43 行增量，不碰主题源码，升级无忧，不想要了整段删掉即可。配合之前加的 sitemap 和 RSS，博客这波算是里外都收拾顺了。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>AI编码工具使用心得</title>
      <link>https://log1992.cn/2026/08/19/play/AI%E7%BC%96%E7%A0%81%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E5%BF%83%E5%BE%97/</link>
      <description>
        <![CDATA[<blockquote>
<p>从最早觉得「AI 写代码就是玩具」，到现在日常开发离不开，差不多两年了。这篇记录下我实际的使用场景、提问技巧、踩过的坑和一些个人心得，不吹不黑。</p>
</blockquote>
<h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>我算接触比较晚的那批人。写了这么多年 Andr]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/AI/">AI</category>
      <category domain="https://log1992.cn/tags/ZCode/">ZCode</category>
      <category domain="https://log1992.cn/tags/%E6%95%88%E7%8E%87/">效率</category>
      <pubDate>Wed, 19 Aug 2026 05:30:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>从最早觉得「AI 写代码就是玩具」，到现在日常开发离不开，差不多两年了。这篇记录下我实际的使用场景、提问技巧、踩过的坑和一些个人心得，不吹不黑。</p></blockquote><h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>我算接触比较晚的那批人。写了这么多年 Android，习惯的是遇到问题先搜索，StackOverflow 翻一圈，实在不行啃源码。那个年代的排错流程是「报错 → 搜索 → 翻十个链接 → 大半不相干 → 拼凑答案」，一天下来眼睛都是花的。</p><p>最早试用 AI 的时候，它给我的感觉就是一本正经地胡说八道，API 是编的、参数是错的，我还得一个个去核对，不如自己查来得踏实。</p><p>真正改变想法的是几件小事：</p><ul><li>一个正则表达式，自己写要调试半天，丢给它 10 秒给出能用的</li><li>一段五百行的老代码要重构，让它先解释一遍逻辑，比自己啃快多了</li><li>Crash log 直接贴过去，它能把调用链和可能的原因列出来</li></ul><p>这两年工具形态也是一路在变：</p><ol><li>对话式的 ChatGPT：来回复制粘贴，当个高级搜索引擎用</li><li>补全式的 Copilot：写代码时它在旁边猜你要写什么，Tab 按到手软</li><li>Agent 时代的 ZCode 这类：能自己跑命令、改文件、验证结果，你说需求它干活</li></ol><p>形态越往后，对「怎么用」的要求越高，这也是这篇想聊的重点。</p><h4 id="我的使用场景"><a href="#我的使用场景" class="headerlink" title="我的使用场景"></a>我的使用场景</h4><p><strong>1. 苦力活优先</strong></p><p>样板代码、单元测试、gradle 脚本、正则、shell，这类「会写但写着烦」的活最适合丢给 AI。比如 ViewModel + Repository + 测试的整套结构，自己敲要半小时，AI 一分钟出初稿，改改细节就能用。单元测试尤其推荐：Mock 数据、边界 case 这种自己懒得写全的东西，让它枚举一遍，经常能补上自己没想到的坑。</p><p><strong>2. 排查问题</strong></p><p>以前排查一个 Crash 要翻十个链接，现在把 log 和相关代码一起丢给 AI，它会结合上下文分析，一两轮对话基本能定位方向。上次一个只在特定机型复现的 <code>TransactionTooLargeException</code>，它直接指出是某个页面往 Bundle 里塞了大 JSON，方向一次就对了。方案还是要自己验证，但「翻链接」这个过程是实打实省掉了。</p><p><strong>3. 批量杂活</strong></p><p>这次整理博客就是典型：64 篇文章里，代码块语言标注有大小写不规范的、有标错的，还有旧主题遗留的语法残留。手工改要一两天，AI 半小时全搞定，改完还顺手验证了构建。这种<strong>规则明确、量大、枯燥</strong>的活是 AI 的绝对主场。同理还有批量改包名、统一代码风格、老项目升级依赖这类活。</p><p><strong>4. 看不懂的代码</strong></p><p>接手老项目、看开源库源码的时候，让 AI 先讲一遍整体结构，自己再下进去看细节，比硬啃效率高很多。Dagger 那种生成代码满天飞的框架尤其明显——让 AI 对着生成的代码讲依赖图是怎么连起来的，比自己从 <code>DaggerXXXComponent</code> 里一个一个翻舒服太多了。</p><p><strong>5. 学新东西</strong></p><p>之前学 Harmony 的时候深有体会：官方文档 + AI 组合拳。文档看不懂的段落直接问，让它换个说法、举个 Android 里对应的例子，一遍就通了。相当于随身带了个不嫌你问题蠢的老师。比自己搜博客强在两点：一是能结合你的上下文解释，二是不会搜到五年前的过时文章。</p><p><strong>6. 写文档和周报</strong></p><p>技术人最烦的活之一。代码写完了让它根据改动生成注释、README、甚至是周报的初稿，自己再改成「人话」。它写的初稿结构清楚但一股机器味，正好，结构留下，味道自己调。</p><h4 id="怎么提问才有效"><a href="#怎么提问才有效" class="headerlink" title="怎么提问才有效"></a>怎么提问才有效</h4><p>用久了会发现，AI 输出质量的上限基本由提问质量决定。对比一下：</p><p>差的提问：</p><figure class="highlight text"><table><tr><td class="code"><pre><span class="line">我的 APP 闪退了怎么办</span><br></pre></td></tr></table></figure><p>好的提问：</p><figure class="highlight text"><table><tr><td class="code"><pre><span class="line">Android 12 上闪退，log 如下（附完整堆栈），</span><br><span class="line">相关代码是这个 Activity 的 onCreate（附代码），</span><br><span class="line">只在开启严格模式后复现，Android 10 正常。</span><br><span class="line">帮我分析可能的原因，按可能性排序。</span><br></pre></td></tr></table></figure><p>区别就是上下文。我自己总结的几个要点：</p><ul><li><strong>环境给全</strong>：系统版本、机型、复现步骤、完整的报错，别让它猜</li><li><strong>约束提前说</strong>：「只改这个方法，别动其他代码」「用 Java 不要 Kotlin」「要兼容到 API 21」</li><li><strong>要过程不要只要答案</strong>：让它先分析原因再给方案，方便自己判断靠不靠谱</li><li><strong>一次只问一件事</strong>：一次塞五个问题，大概率每个都答得潦草</li></ul><h4 id="踩过的坑"><a href="#踩过的坑" class="headerlink" title="踩过的坑"></a>踩过的坑</h4><ul><li><strong>幻觉是真实存在的</strong>。早期它给我编过一个根本不存在的 Glide API，参数都对得上号的样子，我还真信了，白调试半天。现在凡是它给的 API，拿不准的一律自己查文档</li><li><strong>过度工程</strong>。问个简单问题，它有时候能给一套设计模式齐全的「完美方案」，抽象一大堆，实际业务就三行代码的事。得学会在提问时就约束住：「用最简单的方式实现」</li><li><strong>会把能跑的代码改坏</strong>。让它重构过一个工具类，主体逻辑改对了，顺手把一个「看起来多余」实际在防并发重入的判断删了，测试没覆盖到，上线才炸。<strong>它眼里没有「不敢动」的代码</strong>，关键逻辑要盯紧</li><li><strong>上下文会丢</strong>。聊了十几轮之后它开始忘记前面的约定，重复问已经说过的东西。复杂任务要拆小，关键约束宁可多强调一遍</li><li><strong>手感变钝</strong>。这个是最近才意识到的：API 细节懒得记了，反正问 AI 快。短期没事，长期看基础的扎实度在下降。现在的对策是核心技能（架构、算法、底层原理）坚持自己写，AI 只接管体力部分</li><li><strong>代码安全</strong>。公司项目的代码往公开的 AI 服务里贴之前想清楚，敏感的密钥、内部协议、商业逻辑能不贴就不贴，或者先做脱敏。这年头数据出了门就不是你的了</li><li><strong>工具之间会打架</strong>。折腾博客时碰到的：本地安全插件把第三方主题里的 MD5 哈希（Gravatar 协议要求的）当漏洞拦下来了，AI 分析说这是误报。机器说是风险不一定真是风险，反过来也一样，<strong>最后还是得人来拍板</strong></li></ul><h4 id="心得小结"><a href="#心得小结" class="headerlink" title="心得小结"></a>心得小结</h4><ul><li>把 AI 当一个<strong>不知疲倦但需要盯着的实习生</strong>，不是当神，产出必须过自己的眼</li><li>提问质量决定回答质量，上下文给足比什么都强</li><li>明确、量大、可验证的活放心交给它；模糊的、要拍板的活自己来</li><li>别跟它较劲。一个问法搞不定就换问法、拆任务，反复追问没有意义</li><li>每次让它干完活，花两分钟看看它改了什么，<strong>看 diff 的习惯比会用 AI 本身更重要</strong></li><li>最先被省掉的是「查资料 + 拼凑」这部分工作，<strong>判断和决策还是自己的</strong>。现在写代码和以前最大的区别：以前是手累，现在是脑子累</li></ul><p>工具一直都在变，但用法内核没变过：你得知道自己要什么，它才帮得上你。至于它以后会变成什么样，我是挺期待的，至少博客的格式整理这种活，我这辈子是不想再手动干了。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>acme.sh 自动续期 SSL 证书</title>
      <link>https://log1992.cn/2026/07/03/play/acme-sh%E8%87%AA%E5%8A%A8%E7%BB%AD%E6%9C%9FSSL%E8%AF%81%E4%B9%A6/</link>
      <description>
        <![CDATA[<blockquote>
<p>域名开启 HTTPS 后，免费证书 90 天就过期，每两个月手动申请 + 部署到云主机和七牛 CDN 实在折磨。本文记录用 acme.sh + 极空间 Docker 搭一套自动续期方案的实践：七牛做到全自动，虚拟主机做到「一张证书 + 提醒」。</p>
</blockquote>
<h4 id="背景"><a href="#背景" class="headerlink]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/Docker/">Docker</category>
      <category domain="https://log1992.cn/tags/SSL/">SSL</category>
      <category domain="https://log1992.cn/tags/acme-sh/">acme.sh</category>
      <category domain="https://log1992.cn/tags/NAS/">NAS</category>
      <pubDate>Fri, 03 Jul 2026 06:30:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>域名开启 HTTPS 后，免费证书 90 天就过期，每两个月手动申请 + 部署到云主机和七牛 CDN 实在折磨。本文记录用 acme.sh + 极空间 Docker 搭一套自动续期方案的实践：七牛做到全自动，虚拟主机做到「一张证书 + 提醒」。</p></blockquote><h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>要保护的域名：</p><ul><li>博客主域 + www：部署在西部数码云峰 A 型<strong>虚拟主机</strong></li><li>两个 CDN 子域：部署在七牛云</li></ul><p>第一个关键认知：<strong>虚拟主机 ≠ 云服务器</strong>。云峰 A 型是虚拟空间，没有 SSH、不能装软件、也碰不到 nginx 配置，证书只能在西部数码控制面板的「SSL 部署」里管理。所以「在主机上装 acme.sh + reload nginx」那一套对它完全无效。</p><p>于是方案拆成两块：</p><ul><li>七牛两个域名 → acme.sh 全自动部署</li><li>西部数码博客 → acme.sh 签发 + 企业微信提醒，手动上传「托管证书」（这是虚拟主机 + 免费前提下的上限）</li></ul><p>签发中心放在极空间 NAS（常开、已跑 Docker），而不是 Mac（合盖就断）或虚拟主机（装不了）。</p><h4 id="极空间部署-acme-sh-容器"><a href="#极空间部署-acme-sh-容器" class="headerlink" title="极空间部署 acme.sh 容器"></a>极空间部署 acme.sh 容器</h4><p><code>docker-compose.yml</code>（路径前缀按你极空间实际，可从已有容器的 compose 里抄）：</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">acme.sh:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">neilpang/acme.sh:latest</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">acme.sh</span></span><br><span class="line">    <span class="attr">command:</span> <span class="string">daemon</span>          <span class="comment"># daemon 模式：常驻 + 内置 cron 自动续期，必须</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">network_mode:</span> <span class="string">host</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/tmp/zfsv3/nvme11/你的极空间ID/data/docker/acme:/acme.sh</span></span><br></pre></td></tr></table></figure><p>数据目录挂到容器 <code>/acme.sh</code>，配置和证书才能持久化（否则容器重建就没了）。</p><h4 id="签发证书"><a href="#签发证书" class="headerlink" title="签发证书"></a>签发证书</h4><p>进容器终端，用 DNSPod 的 DNS 验证签一张泛域名证书（覆盖主域 + 所有子域）：</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">export Tencent_SecretId=&quot;AKIDxxx&quot;</span><br><span class="line">export Tencent_SecretKey=&quot;xxx&quot;</span><br><span class="line">acme.sh --register-account -m you@example.com</span><br><span class="line">acme.sh --set-default-ca --server letsencrypt</span><br><span class="line">acme.sh --issue --dns dns_tencent -d log1992.cn -d &#x27;*.log1992.cn&#x27;</span><br></pre></td></tr></table></figure><p>DNSPod 密钥在腾讯云「访问管理 CAM → API 密钥管理」获取，export 一次后 acme.sh 会存进账户配置，之后续期自动复用。</p><h4 id="七牛自动部署（踩坑点）"><a href="#七牛自动部署（踩坑点）" class="headerlink" title="七牛自动部署（踩坑点）"></a>七牛自动部署（踩坑点）</h4><p>初版想用同一张证书 deploy 到 img 和 image 两个 CDN，结果只有 img 生效。</p><p>原因：<strong>acme.sh 的 qiniu deploy-hook 会把 <code>QINIU_CDN_DOMAIN</code> 持久化存进证书的 conf 文件，一张证书只能存一个 CDN 域名</strong>。两次 deploy 用的是同一张证书，第二次的 image 被第一次保存的 img 覆盖了。</p><p>解法：两个 CDN 域名各签一张独立证书，各自配 deploy-hook：</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">acme.sh --issue --dns dns_tencent -d img.log1992.cn</span><br><span class="line">export QINIU_AK=&quot;你的七牛AK&quot;; export QINIU_SK=&quot;你的七牛SK&quot;</span><br><span class="line">QINIU_CDN_DOMAIN=img.log1992.cn acme.sh --deploy -d img.log1992.cn --deploy-hook qiniu</span><br><span class="line"></span><br><span class="line">acme.sh --issue --dns dns_tencent -d image.log1992.cn</span><br><span class="line">QINIU_CDN_DOMAIN=image.log1992.cn acme.sh --deploy -d image.log1992.cn --deploy-hook qiniu</span><br></pre></td></tr></table></figure><p>配置后，这两张证书每次续期都会自动重新部署到对应七牛域名，无需干预。</p><h4 id="博客证书-企业微信提醒"><a href="#博客证书-企业微信提醒" class="headerlink" title="博客证书 + 企业微信提醒"></a>博客证书 + 企业微信提醒</h4><p>博客那张泛域名证书负责输出文件 + 续期提醒。先建输出目录（acme.sh 不会自动建父目录）：</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">mkdir -p /acme.sh/out</span><br></pre></td></tr></table></figure><p>把通知脚本挂到续期后执行：</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">acme.sh --install-cert -d log1992.cn \</span><br><span class="line">  --key-file       /acme.sh/out/blog.key \</span><br><span class="line">  --fullchain-file /acme.sh/out/blog.fullchain.pem \</span><br><span class="line">  --reloadcmd      &quot;sh /acme.sh/my-deploy.sh&quot;</span><br></pre></td></tr></table></figure><p><code>my-deploy.sh</code>（只发企业微信提醒，七牛已交给各自证书的 hook）：</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/sh</span></span><br><span class="line">WECOM=&quot;https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的BOT_KEY&quot;</span><br><span class="line">curl -sS -X POST &quot;$WECOM&quot; -H &#x27;Content-Type: application/json&#x27; \</span><br><span class="line">  -d &#x27;&#123;&quot;msgtype&quot;:&quot;text&quot;,&quot;text&quot;:&#123;&quot;content&quot;:&quot;✅博客证书已续期，请去西部数码上传托管证书&quot;&#125;&#125;&#x27; &gt;/dev/null</span><br></pre></td></tr></table></figure><blockquote><p><code>install-cert</code> 带 <code>reloadcmd</code>，<strong>首次安装就会立即触发一次</strong>，所以第一次执行就会收到通知——这是正常的，说明 reloadcmd 生效、通知链路通了。</p></blockquote><h4 id="西部数码虚拟主机（半自动）"><a href="#西部数码虚拟主机（半自动）" class="headerlink" title="西部数码虚拟主机（半自动）"></a>西部数码虚拟主机（半自动）</h4><p>虚拟主机的自动部署机制：账户名下只要有覆盖该域名的证书（自签或托管），系统就自动部署。</p><p>收到提醒后：</p><ol><li>极空间文件管理 → <code>docker/acme/out/</code> → 复制 <code>blog.fullchain.pem</code> 和 <code>blog.key</code> 内容</li><li>西部数码 → SSL 证书管理 →「上传托管证书」→ 分别粘贴证书和私钥</li><li>系统自动部署到主域 + www（泛域名证书一张覆盖两个域名）</li></ol><p>注意：西部数码开放的 SSL API 只能「买卖证书」，<strong>没有上传托管证书的接口</strong>，所以虚拟主机这块在免费前提下没法做到全自动。</p><h4 id="运行机制"><a href="#运行机制" class="headerlink" title="运行机制"></a>运行机制</h4><ul><li>检测：容器 cron 每天 1 次</li><li>续期：证书 90 天有效，到期前 30 天自动续 ≈ 每 60 天一次</li><li>每 60 天收一次企业微信提醒，手动上传一次西部数码；七牛两个域名全程自动</li></ul><h4 id="踩坑小结"><a href="#踩坑小结" class="headerlink" title="踩坑小结"></a>踩坑小结</h4><ul><li>企业微信 webhook 的 key 是 UUID，<strong>少一个字符就静默失败</strong>（脚本里 <code>&gt;/dev/null</code> 把错误吞了，排查时先把重定向去掉看响应）</li><li><code>install-cert</code> 的 <code>--key-file</code> &#x2F; <code>--fullchain-file</code> 父目录<strong>不会自动创建</strong>，要先 <code>mkdir</code></li><li>acme.sh 的 deploy-hook <strong>一张证书只能绑一个部署目标</strong>，多个目标要分多张证书</li><li>虚拟主机没有 SSH，别想着装 acme.sh；它的 SSL 只能走控制面板或购买证书</li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>Gradle8.x上传aar到maven仓库</title>
      <link>https://log1992.cn/2026/03/11/android/Gradle8.x%E4%B8%8A%E4%BC%A0aar%E5%88%B0maven%E4%BB%93%E5%BA%93/</link>
      <description>
        <![CDATA[<h3 id="为什么要调整"><a href="#为什么要调整" class="headerlink" title="为什么要调整?"></a>为什么要调整?</h3><blockquote>
<p>Gradle升级后之前将本地library推送到maven的插件从<code>maven</code>变成了<code>maven-publish</code>，两者在使用上有一些区别</p>
</]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/Gradle/">Gradle</category>
      <pubDate>Wed, 11 Mar 2026 01:32:48 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h3 id="为什么要调整"><a href="#为什么要调整" class="headerlink" title="为什么要调整?"></a>为什么要调整?</h3><blockquote><p>Gradle升级后之前将本地library推送到maven的插件从<code>maven</code>变成了<code>maven-publish</code>，两者在使用上有一些区别</p></blockquote><h3 id="变更"><a href="#变更" class="headerlink" title="变更"></a>变更</h3><ol><li><p>修改前</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">uploadArchives&#123;</span><br><span class="line">    configuration = configurations.archives</span><br><span class="line">    repositories &#123;</span><br><span class="line">        mavenDeployer &#123;</span><br><span class="line">            snapshotRepository(<span class="attr">url:</span> MAVEN_REPO_SNAPSHOT_URL) &#123;</span><br><span class="line">                authentication(<span class="attr">userName:</span> NEXUS_USERNAME, <span class="attr">password:</span> NEXUS_PASSWORD)</span><br><span class="line">            &#125;</span><br><span class="line">            repository(<span class="attr">url:</span> MAVEN_REPO_RELEASE_URL) &#123;</span><br><span class="line">                authentication(<span class="attr">userName:</span> NEXUS_USERNAME, <span class="attr">password:</span> NEXUS_PASSWORD)</span><br><span class="line">            &#125;</span><br><span class="line">            pom.project &#123;</span><br><span class="line">                version maven_version.name</span><br><span class="line">                artifactId maven_version.id</span><br><span class="line">                groupId GROUP_ID</span><br><span class="line">                packaging TYPE</span><br><span class="line">                description <span class="string">&quot;XXX -&gt; xxx@163.com&quot;</span></span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>还需要手动编译sourceJar</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task sourcesJar(<span class="attr">type:</span> Jar) &#123;</span><br><span class="line">    from android.sourceSets.main.java.srcDirs</span><br><span class="line">    classifier = <span class="string">&#x27;sources&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">artifacts &#123;</span><br><span class="line">    archives sourcesJar</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用方法直接在右侧Gradle工具栏打开<code>Tasks</code> - <code>upload</code> 双击<code>uploadArchives</code>即可</p><p><img src="https://image.log1992.cn/uPic/image-20260311094307446.png"></p></li></ol><p>等待脚本执行完毕即可在仓库中看到我们需要的aar和其他资源</p><p><img src="https://image.log1992.cn/uPic/image-20260311094429728.png"></p><ol start="2"><li><p>修改后</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">afterEvaluate &#123;</span><br><span class="line">    publishing &#123;</span><br><span class="line">        publications &#123;</span><br><span class="line">            release(MavenPublication) &#123;</span><br><span class="line">                from components.release</span><br><span class="line"></span><br><span class="line">                groupId = GROUP_ID</span><br><span class="line">                artifactId = insQuality_maven_version.id</span><br><span class="line">                version = insQuality_maven_version.name</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        repositories &#123;</span><br><span class="line">            maven &#123;</span><br><span class="line">                allowInsecureProtocol <span class="literal">true</span></span><br><span class="line">                url MAVEN_REPO_RELEASE_URL</span><br><span class="line">                credentials &#123;</span><br><span class="line">                    username = NEXUS_USERNAME</span><br><span class="line">                    password = NEXUS_PASSWORD</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p>直接写<code>from components.release</code>在执行是会报错，因此需要在<code>android</code>节点下新增<code>publishing</code><a href="https://developer.android.com/build/publish-library/configure-pub-variants?hl=zh-cn#groovy">相关配置</a>。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">android &#123;</span><br><span class="line">...</span><br><span class="line">publishing &#123;</span><br><span class="line">        singleVariant(<span class="string">&quot;release&quot;</span>) &#123;</span><br><span class="line">            withSourcesJar()</span><br><span class="line">            withJavadocJar()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之前的<code>sourcesJar</code>方法也不需要了</p><p><img src="https://image.log1992.cn/uPic/image-20260311095005168.png"></p><p>直接双击<code>publish</code>或者选择其他脚本将相关library推送到maven，最终结果如下：</p><p><img src="https://image.log1992.cn/uPic/image-20260311095207994.png"></p><p>比使用旧插件生成的项目更多了。</p><p>最后在补充一个，升级到8.x以后，并且studio更新了，之前我们可以直接获取依赖里面的R文件获取资源数据，并且可以通过switch case使用<code>R.id.xxx</code>，因为<code>id</code>是常量，后续谷歌为了提升编译效率，调整了这些使用限制，我们项目中如果用到比较多的这种用法，需要如下调整：</p><p>在<code>gradle.properties</code>文件中增加两行</p><figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 可以直接使用依赖库的R文件</span></span><br><span class="line"><span class="attr">android.nonTransitiveRClass</span>=<span class="string">false</span></span><br><span class="line"><span class="comment"># 可以只用常量R.id</span></span><br><span class="line"><span class="attr">android.nonFinalResIds</span>=<span class="string">false</span></span><br></pre></td></tr></table></figure><p>另外，升级之后，也不允许在<code>AndroidManifest.xml</code>文件中直接写<code> package=&quot;com.xxx.xxxx&quot;</code>了，需要在<code>build.gradle</code>文件中写<code>namespace &#39;com.xxx.xxxx&#39;</code>,<code>BuildConfig</code>也需要显式开启，在<code>build.gradle</code>的<code>android</code>节点下的<code>buildFeatures</code>里手动开启</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">android &#123;</span><br><span class="line">...</span><br><span class="line">buildFeatures&#123;</span><br><span class="line">        buildConfig <span class="literal">true</span></span><br><span class="line">        viewBinding <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>Bitmap操作像素 2</title>
      <link>https://log1992.cn/2025/04/08/android/Bitmap%E6%93%8D%E4%BD%9C%E5%83%8F%E7%B4%A0%202/</link>
      <description>
        <![CDATA[<blockquote>
<p>之前有处理过bitmap重新计算像素点颜色，之前采用的copyPixelsToBuffer方法替换setPixel，效率提升很大，但是最近AI给我提供了一个新方法，更快了。</p>
</blockquote>
<h3 id="setPixel方法"><a href="#setPixel方法" class="headerlink" title="setPixel方法"]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/bitmap/">bitmap</category>
      <category domain="https://log1992.cn/tags/%E5%83%8F%E7%B4%A0/">像素</category>
      <pubDate>Tue, 08 Apr 2025 08:44:21 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>之前有处理过bitmap重新计算像素点颜色，之前采用的copyPixelsToBuffer方法替换setPixel，效率提升很大，但是最近AI给我提供了一个新方法，更快了。</p></blockquote><h3 id="setPixel方法"><a href="#setPixel方法" class="headerlink" title="setPixel方法"></a>setPixel方法</h3><p>我们首先遍历bitmap所有的像素点，通过<code>getPixel()</code>获取当前像素点的颜色，然后根据特定算法，计算出最终应该展示的实际颜色。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> y1; i &lt; y2; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> x2; j &lt; x1; j++) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">oldColor</span> <span class="operator">=</span> compositeBitmap.getPixel(i, j);</span><br><span class="line">                <span class="type">int</span> <span class="variable">alpha</span> <span class="operator">=</span> Color.alpha(oldColor);</span><br><span class="line">                <span class="type">int</span> <span class="variable">defaultAlpha</span> <span class="operator">=</span> Color.alpha(defaultColor);</span><br><span class="line">                <span class="keyword">if</span> (alpha == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="type">int</span> <span class="variable">rollPass</span> <span class="operator">=</span> Math.round(((<span class="type">float</span>) (alpha / defaultAlpha)));</span><br><span class="line">                <span class="keyword">if</span> (rollPass == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (rollPass &gt; mMaxRollPass) &#123;</span><br><span class="line">                    rollPass = mMaxRollPass;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//region 计算面积</span></span><br><span class="line">                <span class="type">Integer</span> <span class="variable">area</span> <span class="operator">=</span> mTrackPointAreaMap.get(rollPass);</span><br><span class="line">                <span class="keyword">if</span> (area == <span class="literal">null</span>) &#123;</span><br><span class="line">                    area = <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                area++;</span><br><span class="line">                mTrackPointAreaMap.put(rollPass, area);</span><br><span class="line">                <span class="comment">//endregion</span></span><br><span class="line">                <span class="type">int</span> <span class="variable">newColor</span> <span class="operator">=</span> RollingMonitorUtil.GetMappingColor(mCurrentMode == MODE_ROLLING ? mMaxRollPass : mMaxVibrateRollPass, rollPass);</span><br><span class="line">                compositeBitmap.setPixel(i, j, newColor);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>在像素较多时，这个方法会比较耗时，会有性能问题。</p><h3 id="copyPixelsToBuffer方法"><a href="#copyPixelsToBuffer方法" class="headerlink" title="copyPixelsToBuffer方法"></a>copyPixelsToBuffer方法</h3><p>在C#中有<code>LockBits</code>方法加快速度，在Android中我们可以使用<code>copyPixelsToBuffer</code>和<code>copyPixelsFromBuffer</code>来加速像素操作。</p><p>查看源码，我们看到最主要的其实是这个方法：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">nativeCopyPixelsToBuffer(mNativePtr, dst);</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title function_">nativeCopyPixelsToBuffer</span><span class="params">(<span class="type">long</span> nativeBitmap,</span></span><br><span class="line"><span class="params">                                                        Buffer dst)</span>;</span><br></pre></td></tr></table></figure><p>它是一个<code>native</code>修饰的方法，即<code>JNI</code>调用的方法，使用底层调用效率更高。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> compositeBitmap.getRowBytes() * compositeBitmap.getHeight() * <span class="number">4</span>;</span><br><span class="line">        <span class="type">ByteBuffer</span> <span class="variable">buf</span> <span class="operator">=</span> ByteBuffer.allocate(size);</span><br><span class="line">        compositeBitmap.copyPixelsToBuffer(buf);</span><br><span class="line">        <span class="type">byte</span>[] byt = buf.array();</span><br><span class="line">        <span class="type">int</span> <span class="variable">defaultAlpha</span> <span class="operator">=</span> Color.alpha(defaultColor);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">ctr</span> <span class="operator">=</span> <span class="number">0</span>; ctr &lt; size; ctr += <span class="number">4</span>) &#123;</span><br><span class="line">            <span class="comment">//access array in form of argb. for ex. byt[0] is &#x27;r&#x27;, byt[1] is &#x27;g&#x27; and so on..</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> byt[ctr] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">g</span> <span class="operator">=</span> byt[ctr + <span class="number">1</span>] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> byt[ctr + <span class="number">2</span>] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> byt[ctr + <span class="number">3</span>] &amp; <span class="number">0xff</span>;</span><br><span class="line">            <span class="keyword">if</span> (a == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">int</span> <span class="variable">rollPass</span> <span class="operator">=</span> Math.round(((<span class="type">float</span>) (a / defaultAlpha)));</span><br><span class="line">            <span class="keyword">if</span> (rollPass == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">int</span> <span class="variable">maxRollPass</span> <span class="operator">=</span> mCurrentMode == MODE_ROLLING ? mMaxRollPass : mMaxVibrateRollPass;</span><br><span class="line">            <span class="keyword">if</span> (rollPass &gt; maxRollPass) &#123;</span><br><span class="line">                rollPass = maxRollPass;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//region 计算面积</span></span><br><span class="line">            <span class="type">Integer</span> <span class="variable">area</span> <span class="operator">=</span> mTrackPointAreaMap.get(rollPass);</span><br><span class="line">            <span class="keyword">if</span> (area == <span class="literal">null</span>) &#123;</span><br><span class="line">                area = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            area++;</span><br><span class="line">            mTrackPointAreaMap.put(rollPass, area);</span><br><span class="line">            <span class="type">int</span> <span class="variable">newColor</span> <span class="operator">=</span> mCurrentMode == MODE_ROLLING ? mRollColorPalette[rollPass].getColor() : mVibrateColorPalette[rollPass].getColor();</span><br><span class="line">            byt[ctr] = (<span class="type">byte</span>) Color.red(newColor);</span><br><span class="line">            byt[ctr + <span class="number">1</span>] = (<span class="type">byte</span>) Color.green(newColor);</span><br><span class="line">            byt[ctr + <span class="number">2</span>] = (<span class="type">byte</span>) Color.blue(newColor);</span><br><span class="line">            byt[ctr + <span class="number">3</span>] = (<span class="type">byte</span>) Color.alpha(newColor);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">ByteBuffer</span> <span class="variable">retBuf</span> <span class="operator">=</span> ByteBuffer.wrap(byt);</span><br><span class="line">        compositeBitmap.copyPixelsFromBuffer(retBuf);</span><br></pre></td></tr></table></figure><p>因为我构建的bitmap采用的是以下方法：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Bitmap</span> <span class="variable">compositeBitmap</span> <span class="operator">=</span> Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);</span><br></pre></td></tr></table></figure><p>我们使用了ARGB_8888，每个像素占了4位，可以通过下面方法获取数组大小：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">bitmap.getWidth() * bitmap.getHeight() * <span class="number">4</span></span><br></pre></td></tr></table></figure><p>循环遍历后取到RGB和相应的透明度，然后就可以针对当前的颜色做统计或者是批量转换操作了。</p><h3 id="setPixelf方法进阶版"><a href="#setPixelf方法进阶版" class="headerlink" title="setPixelf方法进阶版"></a>setPixelf方法进阶版</h3><p>减少在循环中的计算操作，通过预生成的调色板,优化整数运算代替浮点运算，降低计算复杂度，减少计算量</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">width</span> <span class="operator">=</span> compositeBitmap.getWidth();</span><br><span class="line">       <span class="type">int</span> <span class="variable">height</span> <span class="operator">=</span> compositeBitmap.getHeight();</span><br><span class="line">       <span class="type">int</span>[] pixels = <span class="keyword">new</span> <span class="title class_">int</span>[width * height];</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 获取像素数据</span></span><br><span class="line">       compositeBitmap.getPixels(pixels, <span class="number">0</span>, width, <span class="number">0</span>, <span class="number">0</span>, width, height);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 确定当前模式参数</span></span><br><span class="line">       <span class="type">int</span> <span class="variable">maxRollPass</span> <span class="operator">=</span> (mCurrentMode == MODE_VIBRATE) ? mMaxVibrateRollPass : mMaxRollPass;</span><br><span class="line">       <span class="type">int</span> <span class="variable">defaultAlpha</span> <span class="operator">=</span> Color.alpha(defaultColor);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 预生成颜色调色板</span></span><br><span class="line">       <span class="type">int</span>[] colorPalette = <span class="keyword">new</span> <span class="title class_">int</span>[maxRollPass + <span class="number">1</span>];</span><br><span class="line">       <span class="keyword">if</span> (mVibrateColorPalette != <span class="literal">null</span> &amp;&amp; mRollColorPalette != <span class="literal">null</span>) &#123;</span><br><span class="line">           <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">rp</span> <span class="operator">=</span> <span class="number">0</span>; rp &lt;= maxRollPass; rp++) &#123;</span><br><span class="line">               <span class="type">int</span> <span class="variable">color</span> <span class="operator">=</span> (mCurrentMode == MODE_VIBRATE)</span><br><span class="line">                       ? mVibrateColorPalette[rp].getColor()</span><br><span class="line">                       : mRollColorPalette[rp].getColor();</span><br><span class="line">               colorPalette[rp] = color;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 使用数组进行面积统计</span></span><br><span class="line">       <span class="type">int</span>[] areaCounts = <span class="keyword">new</span> <span class="title class_">int</span>[maxRollPass + <span class="number">1</span>];</span><br><span class="line">       <span class="type">int</span> <span class="variable">halfDefaultAlpha</span> <span class="operator">=</span> defaultAlpha / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 处理每个像素</span></span><br><span class="line">       <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; pixels.length; i++) &#123;</span><br><span class="line">           <span class="type">int</span> <span class="variable">alpha</span> <span class="operator">=</span> (pixels[i] &gt;&gt; <span class="number">24</span>) &amp; <span class="number">0xFF</span>;</span><br><span class="line">           <span class="keyword">if</span> (alpha == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 优化整数运算代替浮点运算</span></span><br><span class="line">           <span class="type">int</span> <span class="variable">rollPass</span> <span class="operator">=</span> (alpha + halfDefaultAlpha) / defaultAlpha;</span><br><span class="line">           <span class="keyword">if</span> (rollPass == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 限制最大遍数</span></span><br><span class="line">           <span class="keyword">if</span> (rollPass &gt; maxRollPass) rollPass = maxRollPass;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 统计面积</span></span><br><span class="line">           areaCounts[rollPass]++;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 直接使用预生成的颜色</span></span><br><span class="line">           pixels[i] = colorPalette[rollPass];</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 转换统计结果到Map</span></span><br><span class="line">       <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">rp</span> <span class="operator">=</span> <span class="number">0</span>; rp &lt; areaCounts.length; rp++) &#123;</span><br><span class="line">           <span class="keyword">if</span> (areaCounts[rp] &gt; <span class="number">0</span>) &#123;</span><br><span class="line">               mTrackPointAreaMap.put(rp, areaCounts[rp]);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 写回修改后的像素数据</span></span><br><span class="line">       compositeBitmap.setPixels(pixels, <span class="number">0</span>, width, <span class="number">0</span>, <span class="number">0</span>, width, height);</span><br></pre></td></tr></table></figure><p>通过上述调整，可以将绘制时间从900ms 缩减到 180ms.</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Gradle文件操作</title>
      <link>https://log1992.cn/2024/06/27/develop/Gradle%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C/</link>
      <description>
        <![CDATA[<blockquote>
<p>几乎每个构建都涉及到文件的操作。因此，为了方便文件操作，Gradle专门设计了操作文件的API。</p>
</blockquote>
<p>这些API包含两部分：</p>
<ul>
<li>指定要处理的文件和目录</li>
<li>指定要如何处理文件和目录</li>
</ul>
<h3 id="拷贝文件（Copy）"><a href="#拷贝文件（Copy）" cl]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/categories/android/Gradle/">Gradle</category>
      <category domain="https://log1992.cn/tags/Gradle/">Gradle</category>
      <pubDate>Thu, 27 Jun 2024 03:30:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>几乎每个构建都涉及到文件的操作。因此，为了方便文件操作，Gradle专门设计了操作文件的API。</p></blockquote><p>这些API包含两部分：</p><ul><li>指定要处理的文件和目录</li><li>指定要如何处理文件和目录</li></ul><h3 id="拷贝文件（Copy）"><a href="#拷贝文件（Copy）" class="headerlink" title="拷贝文件（Copy）"></a>拷贝文件（Copy）</h3><p>将特定文件拷贝到指定目录。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task copyCache(<span class="attr">type:</span>Copy)&#123;</span><br><span class="line">    from file(<span class="string">&quot;$&#123;buildDir&#125;/cache/hello.txt&quot;</span>)</span><br><span class="line">    into file(<span class="string">&quot;$&#123;buildDir&#125;/log&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>Project.file()</code>方法是用于创建当前项目下的一个文件或者目录。</p><p>我们也可以不使用<code>file()</code>方法，直接使用路径。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task copyCache2(<span class="attr">type:</span>Copy)&#123;</span><br><span class="line">    from <span class="string">&quot;$&#123;buildDir&#125;/cache/hello.txt&quot;</span></span><br><span class="line">    into <span class="string">&quot;$&#123;buildDir&#125;/log2&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然，我们也可以同时拷贝多个文件到指定目录。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task copyFiles(<span class="attr">type:</span>Copy)&#123;</span><br><span class="line">    from <span class="string">&quot;$&#123;buildDir&#125;/cache/hello.txt&quot;</span>, <span class="string">&quot;$&#123;buildDir&#125;/generated/1.txt&quot;</span></span><br><span class="line">    into <span class="string">&quot;$&#123;buildDir&#125;/log3&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>现在考虑另外一种情况，我们需要拷贝指定目录下的pdf格式的文件。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task copyPdfReportsForArchiving(<span class="attr">type:</span> Copy) &#123;</span><br><span class="line">    from <span class="string">&quot;$&#123;buildDir&#125;/reports&quot;</span></span><br><span class="line">    include <span class="string">&quot;*.pdf&quot;</span></span><br><span class="line">    into <span class="string">&quot;$&#123;buildDir&#125;/toArchive&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://image.log1992.cn/uPic7819453-ae91a5fbc76f1782.webp"></p><p>我们也可以拷贝reports目录以及它的子目录下的pdf文件。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task copyAllPdfReportsForArchiving(<span class="attr">type:</span> Copy) &#123;</span><br><span class="line">    from <span class="string">&quot;$&#123;buildDir&#125;/reports&quot;</span></span><br><span class="line">    include <span class="string">&quot;**/*.pdf&quot;</span></span><br><span class="line">    into <span class="string">&quot;$&#123;buildDir&#125;/toArchive&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://image.log1992.cn/uPic7819453-2fea4227ce95e3dc.webp"></p><h3 id="创建归档文件（zip，tar等）"><a href="#创建归档文件（zip，tar等）" class="headerlink" title="创建归档文件（zip，tar等）"></a>创建归档文件（zip，tar等）</h3><p>Gradle 提供了Zip，Tar，Jar等任务，可以将文件打包成<code>.zip</code>，<code>.tar</code>或者<code>.jar</code>格式的文件。</p><p>例如，我们可以将<code>${buildDir}/toArchive</code>目录下的文件，打包成<code>${buildDir}/dist</code>目录的<code>my-distribution.zip</code>文件</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task packageDistribution(<span class="attr">type:</span> Zip) &#123;</span><br><span class="line">    archiveName = <span class="string">&quot;my-distribution.zip&quot;</span></span><br><span class="line">    destinationDir = file(<span class="string">&quot;$&#123;buildDir&#125;/dist&quot;</span>)</span><br><span class="line"></span><br><span class="line">    from <span class="string">&quot;$&#123;buildDir&#125;/toArchive&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="打开档案文件"><a href="#打开档案文件" class="headerlink" title="打开档案文件"></a>打开档案文件</h3><p><code>Project.zipTree</code>和<code>Project.tarTree</code>方法可以从相应的归档文件生成<code>FileTree</code>。<code>FileTree</code>可以用在from方法中。如下所示。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task unpackFiles(<span class="attr">type:</span> Copy) &#123;</span><br><span class="line">    from zipTree(<span class="string">&quot;$&#123;buildDir&#125;/dist/my-distribution.zip&quot;</span>)</span><br><span class="line">    into <span class="string">&quot;$&#123;buildDir&#125;/resources&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="创建目录"><a href="#创建目录" class="headerlink" title="创建目录"></a>创建目录</h3><p>我们可以使用<code>Project.mkdir</code>方法创建目录。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task ensureDirectory &#123;</span><br><span class="line">    doLast &#123;</span><br><span class="line">        mkdir <span class="string">&quot;images&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="移动文件或目录"><a href="#移动文件或目录" class="headerlink" title="移动文件或目录"></a>移动文件或目录</h3><p>Gradle并没有提供移动文件或者目录的方法。不过我们可以使用<code>ant</code>的方法。</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task moveReports &#123;</span><br><span class="line">    doLast &#123;</span><br><span class="line">        ant.move <span class="attr">file:</span> <span class="string">&quot;$&#123;buildDir&#125;/reports&quot;</span>,</span><br><span class="line">                 <span class="symbol">todir:</span> <span class="string">&quot;$&#123;buildDir&#125;/toArchive&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="删除文件和目录"><a href="#删除文件和目录" class="headerlink" title="删除文件和目录"></a>删除文件和目录</h3><p>在Gradle中，可以使用Delete任务或者<code>Project.delete</code>方法删除文件或者目录。</p><h4 id="删除目录"><a href="#删除目录" class="headerlink" title="删除目录"></a>删除目录</h4><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task myClean(<span class="attr">type:</span> Delete) &#123;</span><br><span class="line">    delete buildDir</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="删除指定文件"><a href="#删除指定文件" class="headerlink" title="删除指定文件"></a>删除指定文件</h4><p>如果想要控制删除哪些文件，我们可以使用<code>FileCollection</code>和<code>FileTree</code>。</p><p>删除源文件夹下的临时文件</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">task cleanTempFiles(<span class="attr">type:</span> Delete) &#123;</span><br><span class="line">    delete fileTree(<span class="string">&quot;src&quot;</span>).matching &#123;</span><br><span class="line">        include <span class="string">&quot;**/*.tmp&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>Groovy语言处理Json</title>
      <link>https://log1992.cn/2024/06/25/develop/Groovy%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86Json/</link>
      <description>
        <![CDATA[<blockquote>
<p>最近处理Jenkins自动化编译，自己手动写了个打包脚本，需要对Json进行操作。</p>
</blockquote>
<h3 id="Json反序列化"><a href="#Json反序列化" class="headerlink" title="Json反序列化"></a>Json反序列化</h3><p>由于在gradle文件中无法调用外部Gson，所以采用gro]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/categories/android/Groovy/">Groovy</category>
      <category domain="https://log1992.cn/tags/Json/">Json</category>
      <pubDate>Tue, 25 Jun 2024 05:54:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>最近处理Jenkins自动化编译，自己手动写了个打包脚本，需要对Json进行操作。</p></blockquote><h3 id="Json反序列化"><a href="#Json反序列化" class="headerlink" title="Json反序列化"></a>Json反序列化</h3><p>由于在gradle文件中无法调用外部Gson，所以采用groovy自带的Json处理</p><p>导包</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> groovy.json.JsonSlurper</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> outJsonConfig = <span class="keyword">new</span> JsonSlurper().parseText(jsonStr)</span><br></pre></td></tr></table></figure><p>jsonStr的值</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;artifactType&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;APK&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;kind&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Directory&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;applicationId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.xxxx.xxx&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;variantName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;huaceDebug&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;elements&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;SINGLE&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;filters&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;versionCode&quot;</span><span class="punctuation">:</span> <span class="number">28</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;versionName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;28&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;enabled&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;outputFile&quot;</span><span class="punctuation">:</span> <span class="string">&quot;xxx.debug.v1.0.28.20240619.apk&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>序列化的结果</p><p><img src="https://image.log1992.cn/uPicimage-20240625150348755.png"></p><h4 id="Json序列化"><a href="#Json序列化" class="headerlink" title="Json序列化"></a>Json序列化</h4><p>两种方法</p><h4 id="JsonBuilder"><a href="#JsonBuilder" class="headerlink" title="JsonBuilder"></a>JsonBuilder</h4><p>导包</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> groovy.json.JsonBuilder</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> generator = <span class="keyword">new</span> JsonGenerator.Options()</span><br><span class="line">               .excludeFieldsByName(<span class="string">&#x27;contentHash&#x27;</span>, <span class="string">&#x27;originalClassName&#x27;</span>)</span><br><span class="line">               .build()</span><br><span class="line">       <span class="keyword">def</span> jsonBuilder = <span class="keyword">new</span> JsonBuilder(appConfigObj,generator);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">def</span> appConfigJson = jsonBuilder.toString()</span><br></pre></td></tr></table></figure><p>这里有一个<code>JsonGenerator</code>,主要是因为在将对象序列化成Json时会自动生成两个字段<code>contentHash</code>和<code>originalClassName</code> 并且嵌套对象的每一层都会存在</p><p>为了去除多余的字段，所以使用<code>generator</code>去除</p><p><img src="https://image.log1992.cn/uPicimage-20240625150941099.png"></p><p>得到的Json</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"><span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ok&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;content&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"><span class="attr">&quot;important&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;xxxxxx&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">28</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;AndroidFiles/xxxxxxxx.apk&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;state&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h4 id="JsonOutput"><a href="#JsonOutput" class="headerlink" title="JsonOutput"></a>JsonOutput</h4><p>导包</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> groovy.json.JsonOutput</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> json = JsonOutput.toJson(appConfigObj)</span><br></pre></td></tr></table></figure><p>得到的Json</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"><span class="attr">&quot;contentHash&quot;</span><span class="punctuation">:</span> <span class="string">&quot;6f6ffcd51be2a62dbd1311a085f96e46&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;originalClassName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;JsonBody&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ok&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;content&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"><span class="attr">&quot;contentHash&quot;</span><span class="punctuation">:</span> <span class="string">&quot;5832596251b548949b3903af6e7712c2&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;important&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;originalClassName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;JsonContent&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;pyh.debug.v1.0.28.20240619&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">28</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;AndroidFiles/pyh.debug.v1.0.28.20240619.apk&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;state&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>可以看到有自动生成两个字段<code>contentHash</code>和<code>originalClassName</code></p><p>为了保证数据整洁，还是选用<code>JsonBuilder</code>吧</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>再学Harmony-04</title>
      <link>https://log1992.cn/2024/06/07/harmony/%E5%86%8D%E5%AD%A6Harmony-04/</link>
      <description>
        <![CDATA[<blockquote>
<p>前面总结了鸿蒙开发的相关配置规范和权限，今天来学习三方库和分发</p>
</blockquote>
<h3 id="1-三方库"><a href="#1-三方库" class="headerlink" title="1 三方库"></a>1 三方库</h3><h4 id="1-1-三方库来源"><a href="#1-1-三方库来源" class="headerli]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/harmony/">鸿蒙</category>
      <category domain="https://log1992.cn/tags/Harmony/">Harmony</category>
      <pubDate>Fri, 07 Jun 2024 07:29:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>前面总结了鸿蒙开发的相关配置规范和权限，今天来学习三方库和分发</p></blockquote><h3 id="1-三方库"><a href="#1-三方库" class="headerlink" title="1 三方库"></a>1 三方库</h3><h4 id="1-1-三方库来源"><a href="#1-1-三方库来源" class="headerlink" title="1.1 三方库来源"></a>1.1 三方库来源</h4><h5 id="1-1-1-本地库"><a href="#1-1-1-本地库" class="headerlink" title="1.1.1 本地库"></a>1.1.1 本地库</h5><p>本地库可以是自己编写的，也可以是其他任提供的源码</p><p><img src="https://image.log1992.cn/uPicimage-20240627113903873.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627113910938.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627113920172.png"></p><p>生成后会自动创建一个和entry平级的module</p><p><img src="https://image.log1992.cn/uPicimage-20240627113932042.png"></p><p>查看module目录下的<code>module.json5</code></p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;NewButtons&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;har&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;deviceTypes&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="string">&quot;default&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="string">&quot;tablet&quot;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>library的type是<code>har</code></p><p>entry的type是<code>entry</code></p><p>注意区分</p><h5 id="1-1-2-远程库"><a href="#1-1-2-远程库" class="headerlink" title="1.1.2 远程库"></a>1.1.2 远程库</h5><p>社区库是指已经由贡献者上架到ohpm中心供其他开发者下载使用的库。</p><p>例如@ohos&#x2F;lottie 和 @ohos&#x2F;axios 等等</p><h4 id="1-2-三方库引入"><a href="#1-2-三方库引入" class="headerlink" title="1.2 三方库引入"></a>1.2 三方库引入</h4><h5 id="1-2-1-本地库"><a href="#1-2-1-本地库" class="headerlink" title="1.2.1 本地库"></a>1.2.1 本地库</h5><h6 id="1-2-1-1-方式一"><a href="#1-2-1-1-方式一" class="headerlink" title="1.2.1.1 方式一"></a>1.2.1.1 方式一</h6><p>在Terminal窗口中，执行如下命令进行安装，并会在<code>oh-package.json5</code>中自动添加依赖。</p><p>需要在<code>entry</code>目录下执行</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">ohpm install ../library </span><br></pre></td></tr></table></figure><p><img src="https://image.log1992.cn/uPicimage-20240627114330587.png"></p><p>会自动在<code>entry</code>的<code>on-packageson-package.json5</code>文件下增加依赖</p><p> <img src="https://image.log1992.cn/uPicimage-20240627114345888.png"></p><figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Buttons</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;newbuttons/src/main/ets/components/view/Buttons</span></span><br></pre></td></tr></table></figure><p>通过代码import即可使用了</p><h6 id="1-2-1-2-方式二"><a href="#1-2-1-2-方式二" class="headerlink" title="1.2.1.2 方式二"></a>1.2.1.2 方式二</h6><p>在工程的<code>oh-package.json5</code>中设置HarmonyOS ohpm三方包依赖，配置示例如下：</p><p>module目录下的<code>module.json5</code></p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;license&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;devDependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;author&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;entry&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Please describe the basic information.&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;main&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;dependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;newbuttons&quot;</span><span class="punctuation">:</span> <span class="string">&quot;file:../NewButtons&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>手动增加dependencies，手动编写依赖设置完成后，执行ohpm install命令安装依赖包，依赖包会存储在工程的oh_modules目录下。</p><p><code>newbuttons</code>是自定义名称，<code>&quot;file:../NewButtons&quot;</code>根据实际编写，引用方式同上</p><h5 id="1-2-2-远程库"><a href="#1-2-2-远程库" class="headerlink" title="1.2.2 远程库"></a>1.2.2 远程库</h5><h6 id="1-2-2-1-方式一"><a href="#1-2-2-1-方式一" class="headerlink" title="1.2.2.1 方式一"></a>1.2.2.1 方式一</h6><p>在Terminal窗口中，执行如下命令安装HarmonyOS ohpm三方包，DevEco Studio会自动在工程的<code>oh-package.json5</code>中自动添加三方包依赖。</p> <figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">ohpm install @ohos/lottie</span><br></pre></td></tr></table></figure><p>然后就能直接引用了</p> <figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lottie, &#123; <span class="title class_">AnimationItem</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@ohos/lottie&#x27;</span>;</span><br></pre></td></tr></table></figure><h6 id="1-2-2-2-方式二"><a href="#1-2-2-2-方式二" class="headerlink" title="1.2.2.2 方式二"></a>1.2.2.2 方式二</h6><p>在工程的<code>oh-package.json5</code>中设置HarmonyOS ohpm三方包依赖，配置示例如下：</p> <figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="attr">&quot;dependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">   <span class="attr">&quot;@ohos/lottie&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^2.0.0&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>跟上面一样，需要执行<code>ohpm install</code>命令安装依赖包，依赖包会存储在工程的<code>oh_modules</code>目录下。</p><p>引用方式参照方式一</p><h3 id="2-分发"><a href="#2-分发" class="headerlink" title="2 分发"></a>2 分发</h3><h4 id="2-1-创建账号"><a href="#2-1-创建账号" class="headerlink" title="2.1 创建账号"></a>2.1 创建账号</h4><p>AppGallery Connect（以下简称AGC）是华为应用市场推出的应用一站式服务平台，致力于为开发者提供应用创意、开发、分发、运营、分析全生命周期服务，构建全场景智慧化的应用生态。</p><p>AGC可以为多个平台提供软件分发,包括但不限于Android、iOS、Windows和鸿蒙等</p><p>开发者账号申请流程见AGC官网(AppGallery Connect) </p><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">https://developer.huawei.com/consumer/cn/service/josp/agc/index.html<span class="params">#</span>/</span><br></pre></td></tr></table></figure><h4 id="2-2-创建应用"><a href="#2-2-创建应用" class="headerlink" title="2.2 创建应用"></a>2.2 创建应用</h4><h5 id="2-2-1-新建应用"><a href="#2-2-1-新建应用" class="headerlink" title="2.2.1 新建应用"></a>2.2.1 新建应用</h5><p><img src="https://image.log1992.cn/uPicimage-20240627115226221.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627115237520.png"></p><h5 id="2-2-2-完善应用信息"><a href="#2-2-2-完善应用信息" class="headerlink" title="2.2.2 完善应用信息"></a>2.2.2 完善应用信息</h5><p><img src="https://image.log1992.cn/uPicimage-20240627115302879.png">。</p><p>需要提供应用图标以及截图</p><p>图标需为正方形。尺寸：216*216px；格式：PNG(500 KB 以内)、WEBP(100 KB 以内)。</p><p>截图需上传 3~5 张。建议尺寸（请确保宽高比16:9）：800*450px；格式：PNG、 JPG、 JPEG (均在 2 MB 以内)，以及 WEBP (100 KB 以内)。</p><p>注意图片要求以及数量</p><h4 id="2-3-准备工作"><a href="#2-3-准备工作" class="headerlink" title="2.3 准备工作"></a>2.3 准备工作</h4><h5 id="2-3-1-发布国家地区"><a href="#2-3-1-发布国家地区" class="headerlink" title="2.3.1 发布国家地区"></a>2.3.1 发布国家地区</h5><p><img src="https://image.log1992.cn/uPicimage-20240627115447383.png"></p><p>根据应用自身情况,选择合适的国家或地区</p><h5 id="2-3-2-软件版本"><a href="#2-3-2-软件版本" class="headerlink" title="2.3.2 软件版本"></a>2.3.2 软件版本</h5><p><img src="https://image.log1992.cn/uPicimage-20240627115510480.png"></p><p>在软件包管理中选择当前需要提交的APP文件</p><p><img src="https://image.log1992.cn/uPicimage-20240627115522339.png"></p><h5 id="2-3-3-APP签名"><a href="#2-3-3-APP签名" class="headerlink" title="2.3.3 APP签名"></a>2.3.3 APP签名</h5><p>生成密钥和证书请求文件</p><p><img src="https://image.log1992.cn/uPicimage-20240627115546264.png"></p><p>选择<code>new</code>一个</p><p><img src="https://image.log1992.cn/uPicimage-20240627115606775.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627115614001.png"></p><p>输入<code>key </code>的别名，密码，地区信息</p><p><img src="https://image.log1992.cn/uPicimage-20240627115633155.png"></p><p>随后即可生成csr文件</p><p><img src="https://image.log1992.cn/uPicimage-20240627115647597.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627115653673.png"></p><h5 id="2-3-4-申请证书"><a href="#2-3-4-申请证书" class="headerlink" title="2.3.4 申请证书"></a>2.3.4 申请证书</h5><p>登录AppGallery Connect，选择“用户与访问”。</p><p><img src="https://image.log1992.cn/uPicimage-20240627115719440.png"></p><p>选择“证书管理”，“新增证书”</p><p><img src="https://image.log1992.cn/uPicimage-20240627115748034.png"></p><p>填写相关信息后提交</p><p><img src="https://image.log1992.cn/uPicimage-20240627115829355.png"></p><h5 id="2-3-5-生成HAP-Provision-Profile"><a href="#2-3-5-生成HAP-Provision-Profile" class="headerlink" title="2.3.5 生成HAP Provision Profile"></a>2.3.5 生成HAP Provision Profile</h5><p><img src="https://image.log1992.cn/uPicimage-20240627115914449.png"></p><p>此处需要选择发布证书</p><p><img src="https://image.log1992.cn/uPicimage-20240627115941518.png"></p><h5 id="2-3-6-配置签名"><a href="#2-3-6-配置签名" class="headerlink" title="2.3.6 配置签名"></a>2.3.6 配置签名</h5><p>将证书与app provision文件下载下来</p><p><img src="https://image.log1992.cn/uPicimage-20240627120005142.png"></p><p>分别配置debug和release证书</p><p><img src="https://image.log1992.cn/uPicimage-20240627120019829.png"></p><p>真机调试的时候选择debug</p><p><img src="https://image.log1992.cn/uPicimage-20240627133943705.png"></p><p>需要打包上架AGC时切换为release</p><p><img src="https://image.log1992.cn/uPicimage-20240627134002814.png"></p><p>在project的build目录下会生成app文件</p><p><img src="https://image.log1992.cn/uPicimage-20240627134019336.png"></p><h5 id="2-3-7-软件上传"><a href="#2-3-7-软件上传" class="headerlink" title="2.3.7 软件上传"></a>2.3.7 软件上传</h5><p><img src="https://image.log1992.cn/uPicimage-20240627134042209.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240627134109956.png"></p><p>选取后即可看到文件信息</p><h5 id="2-3-8-隐私政策与备案"><a href="#2-3-8-隐私政策与备案" class="headerlink" title="2.3.8 隐私政策与备案"></a>2.3.8 隐私政策与备案</h5><p>上架市场的APP都需要有隐私政策界面展示</p><p><img src="https://image.log1992.cn/uPicimage-20240627134148273.png"></p><p>需要提供备案信息</p><p><img src="https://image.log1992.cn/uPicimage-20240627134158576.png"></p><h5 id="2-3-9-版权信息"><a href="#2-3-9-版权信息" class="headerlink" title="2.3.9 版权信息"></a>2.3.9 版权信息</h5><p><img src="https://image.log1992.cn/uPicimage-20240627134216570.png"></p><p>需要提供电子版权证书或代理证书</p><h4 id="2-4-上架"><a href="#2-4-上架" class="headerlink" title="2.4 上架"></a>2.4 上架</h4><p><img src="https://image.log1992.cn/uPicimage-20240627134243289.png"></p><p>保存后提交审核即可</p><p><img src="https://image.log1992.cn/uPicimage-20240627134255304.png"></p><p>未完待续…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>再学Harmony-03</title>
      <link>https://log1992.cn/2024/06/05/harmony/%E5%86%8D%E5%AD%A6Harmony-03/</link>
      <description>
        <![CDATA[<blockquote>
<p>前面总结了鸿蒙开发的调试环境配置，今天来看看相关配置规范</p>
</blockquote>
<h3 id="1-配置规范"><a href="#1-配置规范" class="headerlink" title="1 配置规范"></a>1 配置规范</h3><h4 id="1-1-工程结构"><a href="#1-1-工程结构" class="headerlin]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/harmony/">鸿蒙</category>
      <category domain="https://log1992.cn/tags/Harmony/">Harmony</category>
      <pubDate>Wed, 05 Jun 2024 02:09:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>前面总结了鸿蒙开发的调试环境配置，今天来看看相关配置规范</p></blockquote><h3 id="1-配置规范"><a href="#1-配置规范" class="headerlink" title="1 配置规范"></a>1 配置规范</h3><h4 id="1-1-工程结构"><a href="#1-1-工程结构" class="headerlink" title="1.1 工程结构"></a>1.1 工程结构</h4><h5 id="1-1-1-代码目录结构"><a href="#1-1-1-代码目录结构" class="headerlink" title="1.1.1 代码目录结构"></a>1.1.1 代码目录结构</h5><p>目前新建项目可选内置的功能生成新项目，默认采用Stage模型构建</p><p>其目录结构如下</p><p><img src="https://image.log1992.cn/uPicimage-20240621112900734.png"></p><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">├──entry/src/main/ets                        // 代码区</span><br><span class="line">│  ├──common</span><br><span class="line">│  │  ├──constants</span><br><span class="line">│  │  │  └──CommonConstant.ets               // 常量集合文件</span><br><span class="line">│  │  └──utils</span><br><span class="line">│  │     ├──BroadCast.ets                    // 事件发布订阅管理器</span><br><span class="line">│  │     └──Log.ets                          // 日志打印</span><br><span class="line">│  ├──entryability</span><br><span class="line">│  │  └──EntryAbility.ts                     // 应用入口，承载应用的生命周期</span><br><span class="line">│  ├──model</span><br><span class="line">│  │  ├──EventSourceManager.ets              // 事件资源管理器</span><br><span class="line">│  │  ├──TaskInfo.ets                        // 任务信息存放</span><br><span class="line">│  │  └──TaskInitList.ets                    // 初始化数据</span><br><span class="line">│  ├──pages</span><br><span class="line">│  │  ├──ListIndexPage.ets                   // 页面入口</span><br><span class="line">│  │  └──TaskEditPage.ets                    // 编辑任务页</span><br><span class="line">│  ├──view</span><br><span class="line">│  │  ├──CustomDialogView.ets                // 自定义弹窗统一入口</span><br><span class="line">│  │  ├──TaskDetail.ets                      // 任务编辑详情组件</span><br><span class="line">│  │  ├──TaskEditListItem.ets                // 任务编辑详情Item组件</span><br><span class="line">│  │  ├──TaskList.ets                        // 任务列表组件</span><br><span class="line">│  │  └──TaskSettingDialog.ets               // 弹窗组件</span><br><span class="line">│  └──viewmodel</span><br><span class="line">│     ├──FrequencySetting.ets                // 频率范围设置</span><br><span class="line">│     └──TaskTargetSetting.ets               // 任务目标设置</span><br><span class="line">└──entry/src/main/resources</span><br><span class="line">   ├──base</span><br><span class="line">   │  ├──element                             // 字符串以及颜色的资源文件</span><br><span class="line">   │  ├──media                               // 图片等资源文件</span><br><span class="line">   │  └──profile                             // 页面配置文件存放位置</span><br><span class="line">   ├──en<span class="built_in">_</span>US</span><br><span class="line">   │  └──element</span><br><span class="line">   │     └──string.json                      // 英文字符存放位置</span><br><span class="line">   ├──rawfile                                // 大体积媒体资源存放位置</span><br><span class="line">   └──zh<span class="built_in">_</span>CN</span><br><span class="line">       └──element</span><br><span class="line">          └──string.json                     // 中文字符存放位置</span><br></pre></td></tr></table></figure><h5 id="1-1-2资源目录结构"><a href="#1-1-2资源目录结构" class="headerlink" title="1.1.2资源目录结构"></a>1.1.2资源目录结构</h5><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">resources</span><br><span class="line">|---base</span><br><span class="line">|   |---element</span><br><span class="line">|   |   |---string.json</span><br><span class="line">|   |---media</span><br><span class="line">|   |   |---icon.png</span><br><span class="line">|   |---profile</span><br><span class="line">|   |   |---test<span class="built_in">_</span>profile.json</span><br><span class="line">|---en<span class="built_in">_</span>US  // 默认存在的目录，设备语言环境是美式英文时，优先匹配此目录下资源</span><br><span class="line">|   |---element</span><br><span class="line">|   |   |---string.json</span><br><span class="line">|   |---media</span><br><span class="line">|   |   |---icon.png</span><br><span class="line">|   |---profile</span><br><span class="line">|   |   |---test<span class="built_in">_</span>profile.json</span><br><span class="line">|---zh<span class="built_in">_</span>CN  // 默认存在的目录，设备语言环境是简体中文时，优先匹配此目录下资源</span><br><span class="line">|   |---element</span><br><span class="line">|   |   |---string.json</span><br><span class="line">|   |---media</span><br><span class="line">|   |   |---icon.png</span><br><span class="line">|   |---profile</span><br><span class="line">|   |   |---test<span class="built_in">_</span>profile.json</span><br><span class="line">|---en<span class="built_in">_</span>GB-vertical-car-mdpi // 自定义限定词目录示例，由开发者创建</span><br><span class="line">|   |---element</span><br><span class="line">|   |   |---string.json</span><br><span class="line">|   |---media</span><br><span class="line">|   |   |---icon.png</span><br><span class="line">|   |---profile</span><br><span class="line">|   |   |---test<span class="built_in">_</span>profile.json</span><br><span class="line">|---rawfile // 其他类型文件，原始文件形式保存，不会被集成到resources.index文件中。文件名可自定义。</span><br></pre></td></tr></table></figure><h4 id="1-2-应用-组件级配置"><a href="#1-2-应用-组件级配置" class="headerlink" title="1.2 应用&#x2F;组件级配置"></a>1.2 应用&#x2F;组件级配置</h4><p>在开发应用时，需要配置应用的一些标签，例如应用的包名、图标等标识特征的属性。</p><p>图标和标签通常一起配置，可以分为应用图标、应用标签和入口图标、入口标签，分别对应app.json5配置文件和module.json5配置文件中的icon和label标签。应用图标和标签是在设置应用中使用，例如设置应用中的应用列表。入口图标是应用安装完成后在设备桌面上显示出来的，如下图所示。</p><p><img src="https://image.log1992.cn/uPicimage-20240621113821835.png"></p><p>AppScope目录下的app.json5</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;app&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;bundleName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;com.wuhanins.hrlocation&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;vendor&quot;</span><span class="punctuation">:</span> <span class="string">&quot;example&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;versionCode&quot;</span><span class="punctuation">:</span> <span class="number">1000000</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;versionName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;icon&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$media:ins_logo&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;label&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$string:app_name&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>入口图标是以UIAbility为粒度，支持同一个应用存在多个入口图标和标签，点击后进入对应的UIAbility界面。此类图标显示在手机桌面。</p><h5 id="1-2-1-应用包名配置"><a href="#1-2-1-应用包名配置" class="headerlink" title="1.2.1 应用包名配置"></a>1.2.1 应用包名配置</h5><p>应用需要在工程的AppScope目录下的app.json5配置文件中配置bundleName标签，该标签用于标识应用的唯一性。推荐采用反域名形式命名（如com.example.demo，建议第一级为域名后缀com，第二级为厂商&#x2F;个人名，第三级为应用名，也可以多级）。</p><h5 id="1-2-2-应用图标和标签配置"><a href="#1-2-2-应用图标和标签配置" class="headerlink" title="1.2.2 应用图标和标签配置"></a>1.2.2 应用图标和标签配置</h5><p>Stage模型的应用需要配置应用图标和应用标签。应用图标和标签是在设置应用中使用，例如设置应用中的应用列表，会显示出对应的图标和标签。</p><p>应用图标需要在工程的AppScope目录下的app.json5配置文件中配置icon标签。应用图标需配置为图片的资源索引，配置完成后，该图片即为应用的图标。</p><p>应用标签需要在工程的AppScope模块下的app.json5配置文件中配置label标签。标识应用对用户显示的名称，需要配置为字符串资源的索引。</p><p>AppScope目录下的app.json5</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;app&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;icon&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$media:app_icon&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;label&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$string:app_name&quot;</span></span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h5 id="1-2-3-入口图标和标签配置"><a href="#1-2-3-入口图标和标签配置" class="headerlink" title="1.2.3 入口图标和标签配置"></a>1.2.3 入口图标和标签配置</h5><p>Stage模型支持对组件配置入口图标和入口标签。入口图标和入口标签会显示在桌面上。</p><p>入口图标需要在module.json5配置文件中配置，在abilities标签下面有icon标签。例如希望在桌面上显示该UIAbility的图标，则需要在skills标签下面的entities中添加”entity.system.home”、actions中添加”action.system.home”。同一个应用有多个UIAbility配置上述字段时，桌面上会显示出多个图标，分别对应各自的UIAbility。</p><p>module目录下的module.json5</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="attr">&quot;abilities&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="comment">// $开头的为资源值</span></span><br><span class="line">        <span class="attr">&quot;icon&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$media:icon&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;label&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$string:EntryAbility_label&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;skills&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">          <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;entities&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">              <span class="string">&quot;entity.system.home&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;actions&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">              <span class="string">&quot;action.system.home&quot;</span></span><br><span class="line">            <span class="punctuation">]</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>目前开发只有一个入口，应用图标和标签应该与入口图标和标签一致。</p><h5 id="1-2-4-应用版本声明配置"><a href="#1-2-4-应用版本声明配置" class="headerlink" title="1.2.4 应用版本声明配置"></a>1.2.4 应用版本声明配置</h5><p>未完待续…应用版本声明需要在工程的AppScope目录下的app.json5配置文件中配置versionCode标签和versionName标签。versionCode用于标识应用的版本号，该标签值为32位非负整数。此数字仅用于确定某个版本是否比另一个版本更新，数值越大表示版本越高。versionName标签标识版本号的文字描述。</p><p>versionCode每次更新版本需要手动增加</p><p>versionName 需遵循以下原则 </p><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">[v][大版本号][.][过度版本号][.][自增版本号].[当前日期][<span class="built_in">_</span>某天多版本号区分]</span><br></pre></td></tr></table></figure><p><code>[_某天多版本号]</code>为可选项，当天仅有一个版本时可以不需要</p><p>例如：v1.0.27.20240521  v1.0.27.20240521_1</p><h5 id="1-2-5-Module支持的设备类型配置"><a href="#1-2-5-Module支持的设备类型配置" class="headerlink" title="1.2.5 Module支持的设备类型配置"></a>1.2.5 Module支持的设备类型配置</h5><p>Module支持的设备类型需要在module.json5配置文件中配置deviceTypes标签，如果deviceTypes标签中添加了某种设备，则表明当前的Module支持在该设备上运行。</p><p>类似于android项目中的abiFilters</p><p>module目录下的module.json5</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">     <span class="attr">&quot;deviceTypes&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="string">&quot;phone&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="string">&quot;tablet&quot;</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>目前只需处理手机和平板</p><h5 id="1-2-6-Module权限配置"><a href="#1-2-6-Module权限配置" class="headerlink" title="1.2.6 Module权限配置"></a>1.2.6 Module权限配置</h5><p>Module访问系统或其他应用受保护部分所需的权限信息需要在module.json5配置文件中配置requestPermission标签。该标签用于声明需要申请权限的名称、申请权限的原因以及权限使用的场景。</p><p>module目录下的module.json5</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">     <span class="attr">&quot;requestPermissions&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ohos.abilitydemo.permission.PROVIDER&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$string:reason&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;usedScene&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;abilities&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="string">&quot;FormAbility&quot;</span></span><br><span class="line">          <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;when&quot;</span><span class="punctuation">:</span> <span class="string">&quot;inuse&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>“reason”会直接体现在下图红线位置，根据当前应用需求，自行处理即可</p><p><img src="https://image.log1992.cn/uPicimage-20240621114548870.png"></p><h3 id="2-权限"><a href="#2-权限" class="headerlink" title="2 权限"></a>2 权限</h3><h4 id="2-1-权限使用的基本原则"><a href="#2-1-权限使用的基本原则" class="headerlink" title="2.1 权限使用的基本原则"></a>2.1 权限使用的基本原则</h4><p>在进行权限的申请和使用时，需要满足以下基本原则：</p><ol><li>应用申请的权限，都必须有明确、合理的使用场景和功能说明，确保用户能够清晰明了地知道申请权限的目的、场景、用途；禁止诱导、误导用户授权；应用使用权限必须与申请所述一致。</li><li>应用权限申请遵循最小化原则，只申请业务功能所必要的权限，禁止申请不必要的权限。</li><li><del>应用在首次启动时，避免频繁弹窗申请多个权限；权限须在用户使用对应业务功能时动态申请。</del></li><li>用户拒绝授予某个权限时，与此权限无关的其他业务功能应能正常使用，不能影响应用的正常注册或登录。</li><li>业务功能所需要的权限被用户拒绝且禁止后不再提示，当用户主动触发使用此业务功能或为实现业务功能所必须时，应用程序可通过界面内文字引导，让用户主动到“系统设置”中授权。</li><li>当前不允许应用自行定义权限，应用申请的权限应该从已有的权限列表中选择。</li></ol><p>对于第三点，可根据实际情况，保证系统首次授权的完整性，自行决定。</p><h4 id="2-2-权限等级说明"><a href="#2-2-权限等级说明" class="headerlink" title="2.2 权限等级说明"></a>2.2 权限等级说明</h4><p>根据接口所涉数据的敏感程度或所涉能力的安全威胁影响，ATM模块定义了不同开放范围的权限等级来保护用户隐私。</p><h5 id="2-2-1-应用APL等级说明"><a href="#2-2-1-应用APL等级说明" class="headerlink" title="2.2.1 应用APL等级说明"></a>2.2.1 应用APL等级说明</h5><p>元能力权限等级APL（Ability Privilege Level）指的是应用的权限申请优先级的定义，不同APL等级的应用能够申请的权限等级不同。</p><p>应用的等级可以分为三个等级，分别是：</p><table><thead><tr><th>APL级别</th><th>说明</th></tr></thead><tbody><tr><td>system_core等级</td><td>该等级的应用服务提供操作系统核心能力。</td></tr><tr><td>system_basic等级</td><td>该等级的应用服务提供系统基础服务。</td></tr><tr><td>normal等级</td><td>普通应用。</td></tr></tbody></table><h5 id="2-2-2-权限等级说明"><a href="#2-2-2-权限等级说明" class="headerlink" title="2.2.2 权限等级说明"></a>2.2.2 权限等级说明</h5><p>根据权限对于不同等级应用有不同的开放范围，权限类型对应分为以下三种，等级依次提高。</p><p><strong>normal权限</strong></p><p>normal 权限允许应用访问超出默认规则外的普通系统资源。这些系统资源的开放（包括数据和功能）对用户隐私以及其他应用带来的风险很小。</p><p>该类型的权限仅向APL等级为normal及以上的应用开放。</p><p><strong>system_basic权限</strong></p><p>system_basic权限允许应用访问操作系统基础服务相关的资源。这部分系统基础服务属于系统提供或者预置的基础功能，比如系统设置、身份认证等。这些系统资源的开放对用户隐私以及其他应用带来的风险较大。</p><p>该类型的权限仅向APL等级为system_basic及以上的应用开放。</p><p><strong>system_core权限</strong></p><p>system_core权限涉及到开放操作系统核心资源的访问操作。这部分系统资源是系统最核心的底层服务，如果遭受破坏，操作系统将无法正常运行。</p><p>鉴于该类型权限对系统的影响程度非常大，目前暂不向任何三方应用开放。</p><h5 id="2-2-3-权限类型说明"><a href="#2-2-3-权限类型说明" class="headerlink" title="2.2.3 权限类型说明"></a>2.2.3 权限类型说明</h5><p>根据授权方式的不同，权限类型可分为system_grant（系统授权）和user_grant（用户授权）。</p><p><strong>system_grant</strong></p><p>system_grant指的是系统授权类型，在该类型的权限许可下，应用被允许访问的数据不会涉及到用户或设备的敏感信息，应用被允许执行的操作不会对系统或者其他应用产生大的不利影响。</p><p>如果在应用中申请了system_grant权限，那么系统会在用户安装应用时，自动把相应权限授予给应用。应用需要在应用商店的详情页面，向用户展示所申请的system_grant权限列表。</p><p><strong>user_grant</strong></p><p>user_grant指的是用户授权类型，在该类型的权限许可下，应用被允许访问的数据将会涉及到用户或设备的敏感信息，应用被允许执行的操作可能对系统或者其他应用产生严重的影响。</p><p>该类型权限不仅需要在安装包中申请权限，还需要在应用动态运行时，通过发送弹窗的方式请求用户授权。在用户手动允许授权后，应用才会真正获取相应权限，从而成功访问操作目标对象。</p><p>应用需要在应用商店的详情页面，向用户展示所申请的user_grant权限列表。</p><h5 id="2-2-4-不同权限类型的授权流程"><a href="#2-2-4-不同权限类型的授权流程" class="headerlink" title="2.2.4 不同权限类型的授权流程"></a>2.2.4 不同权限类型的授权流程</h5><p><strong>权限申请</strong></p><p> 开发者需要在配置文件中声明目标权限。</p><p><strong>权限授权</strong></p><p>如果目标权限是system_grant类型，开发者在进行权限申请后，系统会在安装应用时自动为其进行权限预授予，开发者不需要做其他操作即可使用权限。</p><p>如果目标权限是user_grant类型，开发者在进行权限申请后，在运行时触发动态弹窗，请求用户授权。</p><p>ohos.permission.LOCATION权限</p><p>申请条件：使用API version 9以下版本的SDK开发的应用，可以直接申请此权限。使用API version 9及API version 9以上版本的SDK开发的应用，需要先申请权限ohos.permission.APPROXIMATELY_LOCATION，才可申请此权限。</p><p>动态请求授权实例</p><p>module目录下的module.json5</p><figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> <span class="title class_">AbilityConstant</span> <span class="keyword">from</span> <span class="string">&#x27;@ohos.app.ability.AbilityConstant&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> hilog <span class="keyword">from</span> <span class="string">&#x27;@ohos.hilog&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">UIAbility</span> <span class="keyword">from</span> <span class="string">&#x27;@ohos.app.ability.UIAbility&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> <span class="title class_">Want</span> <span class="keyword">from</span> <span class="string">&#x27;@ohos.app.ability.Want&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="keyword">type</span> <span class="variable language_">window</span> <span class="keyword">from</span> <span class="string">&#x27;@ohos.window&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Package</span> <span class="keyword">from</span> <span class="string">&#x27;@system.package&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> bundleManager <span class="keyword">from</span> <span class="string">&#x27;@ohos.bundle.bundleManager&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> abilityAccessCtrl, &#123; <span class="title class_">Permissions</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@ohos.abilityAccessCtrl&#x27;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="attr">permissions</span>: <span class="title class_">Array</span>&lt;<span class="title class_">Permissions</span>&gt; = [<span class="string">&#x27;ohos.permission.READ_CALENDAR&#x27;</span>];</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Lift cycle management of Ability.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> <span class="title class_">EntryAbility</span> <span class="keyword">extends</span> <span class="title class_ inherited__">UIAbility</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onCreate</span>(<span class="attr">want</span>: <span class="title class_">Want</span>, <span class="attr">launchParam</span>: <span class="title class_">AbilityConstant</span>.<span class="property">LaunchParam</span>): <span class="built_in">void</span> &#123;</span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onCreate&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onDestroy</span>(): <span class="built_in">void</span> &#123;</span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onDestroy&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onWindowStageCreate</span>(<span class="attr">windowStage</span>: <span class="variable language_">window</span>.<span class="property">WindowStage</span>): <span class="built_in">void</span> &#123;</span><br><span class="line">    <span class="comment">// Main window is created, set main page for this ability</span></span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onWindowStageCreate&#x27;</span>);</span><br><span class="line">    <span class="keyword">let</span> context = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">    <span class="keyword">let</span> atManager = abilityAccessCtrl.<span class="title function_">createAtManager</span>();</span><br><span class="line">    <span class="comment">// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗</span></span><br><span class="line">    atManager.<span class="title function_">requestPermissionsFromUser</span>(context, permissions).<span class="title function_">then</span>(<span class="function">(<span class="params">data</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> <span class="attr">grantStatus</span>: <span class="title class_">Array</span>&lt;<span class="built_in">number</span>&gt; = data.<span class="property">authResults</span>;</span><br><span class="line">      <span class="keyword">let</span> <span class="attr">length</span>: <span class="built_in">number</span> = grantStatus.<span class="property">length</span>;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (grantStatus[i] === <span class="number">0</span>) &#123;</span><br><span class="line">          <span class="comment">// 用户授权，可以继续访问目标操作</span></span><br><span class="line">          <span class="variable language_">this</span>.<span class="title function_">loadSplash</span>(windowStage)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="comment">// 用户拒绝授权，提示用户必须授权才能访问当前页面的功能，并引导用户到系统设置中打开相应的权限</span></span><br><span class="line">          <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// 授权成功</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">loadSplash</span>(windowStage)</span><br><span class="line">    &#125;).<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">`requestPermissionsFromUser failed, code is <span class="subst">$&#123;err.code&#125;</span>, message is <span class="subst">$&#123;err.message&#125;</span>`</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">loadSplash</span>(<span class="params"><span class="attr">windowStage</span>: <span class="variable language_">window</span>.<span class="title class_">WindowStage</span></span>)&#123;</span><br><span class="line">    windowStage.<span class="title function_">loadContent</span>(<span class="string">&quot;pages/SplashScreenPage&quot;</span>, <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (err.<span class="property">code</span>) &#123;</span><br><span class="line">        hilog.<span class="title function_">error</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;Failed to load the content. Cause: %&#123;public&#125;s&#x27;</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(err) ?? <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;Succeeded in loading the content. Data: %&#123;public&#125;s&#x27;</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(data) ?? <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onWindowStageDestroy</span>(): <span class="built_in">void</span> &#123;</span><br><span class="line">    <span class="comment">// Main window is destroyed, release UI related resources</span></span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onWindowStageDestroy&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onForeground</span>(): <span class="built_in">void</span> &#123;</span><br><span class="line">    <span class="comment">// Ability has brought to foreground</span></span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onForeground&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">onBackground</span>(): <span class="built_in">void</span> &#123;</span><br><span class="line">    <span class="comment">// Ability has back to background</span></span><br><span class="line">    hilog.<span class="title function_">info</span>(<span class="number">0x0000</span>, <span class="string">&#x27;testTag&#x27;</span>, <span class="string">&#x27;%&#123;public&#125;s&#x27;</span>, <span class="string">&#x27;Ability onBackground&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处提供官方权限列表及说明 </p><p><a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/permission-list-0000001544464017-V2">https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/permission-list-0000001544464017-V2</a></p><p>未完待续…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>再学Harmony-02</title>
      <link>https://log1992.cn/2024/06/03/harmony/%E5%86%8D%E5%AD%A6Harmony-02/</link>
      <description>
        <![CDATA[<blockquote>
<p>前面总结了鸿蒙开发环境的搭建与配置，今天来看看调试环境的处理</p>
</blockquote>
<h3 id="1-调试环境"><a href="#1-调试环境" class="headerlink" title="1 调试环境"></a>1 调试环境</h3><h4 id="1-1-配置HDC工具环境变量"><a href="#1-1-配置HDC工具环境变量"]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/harmony/">鸿蒙</category>
      <category domain="https://log1992.cn/tags/Harmony/">Harmony</category>
      <pubDate>Mon, 03 Jun 2024 05:49:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>前面总结了鸿蒙开发环境的搭建与配置，今天来看看调试环境的处理</p></blockquote><h3 id="1-调试环境"><a href="#1-调试环境" class="headerlink" title="1 调试环境"></a>1 调试环境</h3><h4 id="1-1-配置HDC工具环境变量"><a href="#1-1-配置HDC工具环境变量" class="headerlink" title="1.1 配置HDC工具环境变量"></a>1.1 配置HDC工具环境变量</h4><p>HDC是为开发者提供HarmonyOS应用&#x2F;服务的调试工具，全称为HarmonyOS Device Connector。为方便使用HDC工具，需要为HDC端口号设置环境变量。</p><p>HDC的功能类似ADB调试工具。</p><h5 id="1-1-1-配置toolchains环境变量"><a href="#1-1-1-配置toolchains环境变量" class="headerlink" title="1.1.1 配置toolchains环境变量"></a>1.1.1 配置toolchains环境变量</h5><p>hdc工具通过HarmonyOS SDK获取，存放于SDK的toolchains目录下，使用时将SDK的toolchains目录添加到环境变量。</p><p><img src="https://image.log1992.cn/uPicimage-20240620170446868.png"></p><h5 id="1-1-2-配置HDC端口"><a href="#1-1-2-配置HDC端口" class="headerlink" title="1.1.2 配置HDC端口"></a>1.1.2 配置HDC端口</h5><p>在此电脑 &gt; 属性 &gt; 高级系统设置 &gt; 高级 &gt; 环境变量中，添加HDC端口变量名为：HDC_SERVER_PORT，变量值可设置为任意未被占用的端口，如7035。</p><p><img src="https://image.log1992.cn/uPicimage-20240620170535260.png"></p><p>做完上述配置后，打开powershell 或者cmd命令行</p><p>输入 hdc h </p><p><img src="https://image.log1992.cn/uPicimage-20240620170550585.png"></p><p>或者输入hdc -v 查看是否显示当前hdc版本</p><p><img src="https://image.log1992.cn/uPicimage-20240620170602407.png"></p><p>通过hdc list targets 查看当前连接设备</p><p><img src="https://image.log1992.cn/uPicimage-20240620170619362.png"></p><p>至此，hdc调试工具配置完成。</p><h5 id="1-1-3-调试注意事项"><a href="#1-1-3-调试注意事项" class="headerlink" title="1.1.3 调试注意事项"></a>1.1.3 调试注意事项</h5><p>目前工程可以运行在模拟器中，或者运行在真机设备中。如果选择运行在真机设备中，需要先对工程进行签名，然后才能运行在真机设备中。</p><p><img src="https://image.log1992.cn/uPicimage-20240620170707909.png"></p><p>选择Sign In跳转登陆华为开发者账号</p><p><img src="https://image.log1992.cn/uPicimage-20240620170718113.png"></p><p>登陆完成后</p><p><img src="https://image.log1992.cn/uPicimage-20240620170931504.png"></p><p>可以看到签名信息后就能使用真机进行调试了。</p><h4 id="1-2-配置ohpm环境变量"><a href="#1-2-配置ohpm环境变量" class="headerlink" title="1.2 配置ohpm环境变量"></a>1.2 配置ohpm环境变量</h4><p>ohpm即open harmony package manager，作为鸿蒙的包管理器，在使用第三方库的时候是需要的 ，下面是ohpm环境变量的配置与验证。</p><h5 id="1-2-1-配置方式"><a href="#1-2-1-配置方式" class="headerlink" title="1.2.1 配置方式"></a>1.2.1 配置方式</h5><p>Windows环境变量设置方法：</p><p>在此电脑 &gt; 属性 &gt; 高级系统设置 &gt; 高级 &gt; 环境变量中，在系统或者用户的PATH变量中，添加ohpm安装位置下bin文件夹的路径。</p><p>macOS环境变量设置方法：</p><p>打开终端工具，执行以下命令。</p><p>export OHPM_HOME&#x3D;&#x2F;home&#x2F;xx&#x2F;Downloads&#x2F;ohpm  #本处路径请替换为ohpm的安装路径</p><p>export PATH&#x3D;$OHPM_HOME&#x2F;bin:$PATH</p><h5 id="1-2-2-检验配置"><a href="#1-2-2-检验配置" class="headerlink" title="1.2.2 检验配置"></a>1.2.2 检验配置</h5><p>代理配置完成后，下载并打开命令行工具，执行如下命令验证网络是否正常。</p><p>ohpm info @ohos&#x2F;lottie</p><p><img src="https://image.log1992.cn/uPicimage-20240620171119378.png"></p><p>执行后输出以上类似的内容即说明处理完成</p><h5 id="1-2-3-设备配置"><a href="#1-2-3-设备配置" class="headerlink" title="1.2.3 设备配置"></a>1.2.3 设备配置</h5><p>设备想要调试应用，需要注册udid</p><p><img src="https://image.log1992.cn/uPicimage-20240620171201918.png"></p><p>通过上述命令获取设备的UDID</p><p>在AGC的设备管理中添加设备</p><p><img src="https://image.log1992.cn/uPicimage-20240620171217527.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240620171227640.png"></p><p>生成调试app provision时选择设备，相关证书配置我们后面再说</p><p>未完待续…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>再学Harmony-01</title>
      <link>https://log1992.cn/2024/06/01/harmony/%E5%86%8D%E5%AD%A6Harmony-01/</link>
      <description>
        <![CDATA[<blockquote>
<p>最近公司做鸿蒙技术预研，于是我基于2024年5月官方文档，写下了部分总结。由于FA模型已被弃用，总结中所有项目都是基于stage模型构建。</p>
</blockquote>
<h3 id="1-开发环境"><a href="#1-开发环境" class="headerlink" title="1 开发环境"></a>1 开发环境</h3><h4 id="1-1-I]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/harmony/">鸿蒙</category>
      <category domain="https://log1992.cn/tags/Harmony/">Harmony</category>
      <pubDate>Sat, 01 Jun 2024 05:49:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>最近公司做鸿蒙技术预研，于是我基于2024年5月官方文档，写下了部分总结。由于FA模型已被弃用，总结中所有项目都是基于stage模型构建。</p></blockquote><h3 id="1-开发环境"><a href="#1-开发环境" class="headerlink" title="1 开发环境"></a>1 开发环境</h3><h4 id="1-1-IDE"><a href="#1-1-IDE" class="headerlink" title="1.1 IDE"></a>1.1 IDE</h4><p>HUAWEI DevEco Studio 是一款面向HarmonyOS应用及元服务开发者提供的集成开发环境(IDE)， 助力高效开发。</p><p>当前总结基于DevEco Studio 3.1.1 Release 编写</p><h5 id="1-1-1-下载"><a href="#1-1-1-下载" class="headerlink" title="1.1.1 下载"></a>1.1.1 下载</h5><p>下载地址：</p><p><a href="https://developer.huawei.com/consumer/cn/deveco-studio#download">https://developer.huawei.com/consumer/cn/deveco-studio#download</a></p><p>目前仅支持 Windows和Mac，该版本适用HarmonyOS和OpenHarmony应用及服务开发，可体验HarmonyOS 3.1 版本及以下的开发能力。</p><p><img src="https://image.log1992.cn/uPicimage-20240620165415320.png"></p><p>特别需要注意的是，Dev-Eco需要依赖node环境，并且3.1.1 Release版本的Dev-Eco只支持14.x到16.x的node版本</p><h5 id="1-1-2-安装"><a href="#1-1-2-安装" class="headerlink" title="1.1.2 安装"></a>1.1.2 安装</h5><p>根据对应的系统下载对应安装文件并安装</p><p>选择路径：</p><p><img src="https://image.log1992.cn/uPicimage-20240620165511812.png"></p><p>增加桌面快捷方式：</p><p><img src="https://image.log1992.cn/uPicimage-20240620165525572.png"></p><p>运行：</p><p><img src="https://image.log1992.cn/uPicimage-20240620165538319.png"></p><h4 id="1-2-依赖"><a href="#1-2-依赖" class="headerlink" title="1.2 依赖"></a>1.2 依赖</h4><h5 id="1-2-1-基础配置"><a href="#1-2-1-基础配置" class="headerlink" title="1.2.1 基础配置"></a>1.2.1 基础配置</h5><p><img src="https://image.log1992.cn/uPicimage-20240620165641120.png"></p><p>Dev-Eco需要依赖node环境，并且3.1.1 Release版本的Dev-Eco只支持14.x到16.x的node版本。</p><p>选择自己已安装好的node版本或者通过ide自动下载推荐的版本。</p><p>这里推荐使用nvm进行本地node环境管理</p><p>下载地址：<a href="https://github.com/coreybutler/nvm-windows">https://github.com/coreybutler/nvm-windows</a></p><p>安装完成后使用 nvm ls查看当前已安装的node环境</p><p><img src="https://image.log1992.cn/uPicimage-20240620165653977.png"></p><p>前面带*的是当前正在使用的版本</p><p>通过 nvm use xxxx 切换其他版本，xxxx为系统中已安装的node版本号</p><p><img src="https://image.log1992.cn/uPicimage-20240620165723329.png"></p><p>查看当前node版本，发现已经切换为20.13.1</p><p><img src="https://image.log1992.cn/uPicimage-20240620165735154.png"></p><p>初始状态使用nvm ls会显示空白，因为系统中没有安装node</p><p>使用nvm install lts 安装最新的LTS版本</p><p>使用nvm install xxxx 安装具体的版本，xxxx为具体版本号，例如：16.19.1</p><p>可以使用nvm use 16 直接切换到node 16 ，因为此时系统中16版本就一个</p><p><img src="https://image.log1992.cn/uPicimage-20240620165748683.png"></p><p>nvm安装默认的目录在C:\Users\Administrator\AppData\Roaming，AppData是隐藏目录，需注意。</p><p><img src="https://image.log1992.cn/uPicimage-20240620165808437.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240620165814510.png"></p><p>这样基本配置就配置好了</p><h5 id="1-2-2-SDK配置"><a href="#1-2-2-SDK配置" class="headerlink" title="1.2.2 SDK配置"></a>1.2.2 SDK配置</h5><p><img src="https://image.log1992.cn/uPicimage-20240620165839757.png"></p><p>设置好自己想保存SDK的目录</p><h5 id="1-2-3-汇总配置"><a href="#1-2-3-汇总配置" class="headerlink" title="1.2.3 汇总配置"></a>1.2.3 汇总配置</h5><p><img src="https://image.log1992.cn/uPicimage-20240620165902473.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240620165909174.png"></p><p>把HarmonyOS SDK和OpenHarmony SDK都勾选了</p><p><img src="https://image.log1992.cn/uPicimage-20240620165927784.png"></p><p>进入下载队列</p><p><img src="https://image.log1992.cn/uPicimage-20240620165940573.png"></p><p>等待环境下载完成</p><p><img src="https://image.log1992.cn/uPicimage-20240620165952445.png"></p><h5 id="1-2-4-环境配置检查"><a href="#1-2-4-环境配置检查" class="headerlink" title="1.2.4 环境配置检查"></a>1.2.4 环境配置检查</h5><p>检查环境配置是否正常</p><p><img src="https://image.log1992.cn/uPicimage-20240620170021308.png"></p><p><img src="https://image.log1992.cn/uPicimage-20240620170027158.png"></p><p>所有的勾都勾上说明目前环境正常。</p><p>未完待续…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>处理TransactionTooLargeException</title>
      <link>https://log1992.cn/2024/04/02/android/%E5%A4%84%E7%90%86TransactionTooLargeException/</link>
      <description>
        <![CDATA[<blockquote>
<p>最近在某些项目上出现了<a href="https://developer.android.google.cn/reference/android/os/TransactionTooLargeException">TransactionTooLargeException</a>，查看崩溃日志后返现并没有给出确切的错误定位。</p>
</blockquote>
<h3]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/TransactionTooLargeException/">TransactionTooLargeException</category>
      <pubDate>Tue, 02 Apr 2024 03:06:33 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>最近在某些项目上出现了<a href="https://developer.android.google.cn/reference/android/os/TransactionTooLargeException">TransactionTooLargeException</a>，查看崩溃日志后返现并没有给出确切的错误定位。</p></blockquote><h3 id="产生原因"><a href="#产生原因" class="headerlink" title="产生原因"></a>产生原因</h3><p><code>TransactionTooLargeException</code>原因分析：在应用层与各种<code>SystemService</code>交互过程中，调用参数和返回值会通过<code>Parcel</code>对象存储在<code>Binder</code> <code>Transaction</code>缓冲区中以进行传输，如果参数或返回值太大超过了<code>Binder`` Transaction</code>缓冲区的限制大小，那么调用将会失败抛出<code>TransactionTooLargeException</code>。在日常开发中，<code>TransactionTooLargeException</code>通常是由于都是<code>传输的数据过大</code>或<code>页面被杀死时缓存的数据过大</code>导致的。</p><h3 id="定位问题"><a href="#定位问题" class="headerlink" title="定位问题"></a>定位问题</h3><p>我们使用<a href="https://github.com/guardian/toolargetool">toolargetool</a>来定位问题</p><figure class="highlight groovy"><table><tr><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">    ...</span><br><span class="line">    implementation <span class="string">&#x27;com.gu.android:toolargetool:0.3.0&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<code>Application.onCreate</code>方法中初始化</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">TooLargeTool.startLogging(<span class="built_in">this</span>);</span><br></pre></td></tr></table></figure><p>使用<code>adb</code>命令打开专用监听窗口</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">adb logcat -s TooLargeTool</span><br></pre></td></tr></table></figure><p><img src="https://image.log1992.cn/uPic/image-20230920154026664.png"></p><p>出现组件正在向事务写入大量数据和缓冲区的时候就会出现日志</p><h3 id="常见的原因"><a href="#常见的原因" class="headerlink" title="常见的原因"></a>常见的原因</h3><ol><li><p>Fragment.setArguments传输的Bundle过大</p><p>我们应特别注意通过<code>Fragment.setArguments</code>所传递的Bundle的数据大小，如果数据量较大，应该选择使用其他方式进行传输。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MemoryBigData</span>&lt;T&gt; <span class="keyword">extends</span> <span class="title class_">ZeroBaseDto</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ConcurrentHashMap&lt;String, Object&gt; CACHE_MAP = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; MemoryBigData&lt;T&gt; <span class="title function_">of</span><span class="params">(T value)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> of(UUID.randomUUID().toString(), value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过tag存储MemoryBigData</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;T&gt;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; MemoryBigData&lt;T&gt; <span class="title function_">of</span><span class="params">(String tag, T value)</span> &#123;</span><br><span class="line">        MemoryBigData&lt;T&gt; bigData = <span class="keyword">new</span> <span class="title class_">MemoryBigData</span>&lt;&gt;(tag);</span><br><span class="line">        bigData.set(value);</span><br><span class="line">        <span class="keyword">return</span> bigData;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String tag;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过tag构造MemoryBigData</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">MemoryBigData</span><span class="params">(String tag)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.tag = tag;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T t)</span> &#123;</span><br><span class="line">        CACHE_MAP.put(tag, t);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过tag获取MemoryBigData</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (T) CACHE_MAP.get(tag);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过tag获取MemoryBigData并删除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">getAndRemove</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (T) CACHE_MAP.remove(tag);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断是否存在tag对应的数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPresent</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> CACHE_MAP.containsKey(tag);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>存储：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Bundle</span> <span class="variable">args</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bundle</span>();</span><br><span class="line">args.putSerializable(MODELS,MemoryBigData.of(models));</span><br></pre></td></tr></table></figure><p>获取：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">MemoryBigData&lt;List&lt;TestDetectionEditViewModel&gt;&gt; bigData = (MemoryBigData) arguments.getSerializable(MODELS);</span><br><span class="line">models = bigData.getAndRemove();</span><br></pre></td></tr></table></figure><p>也可以使用<code>bigData.get()</code>，不过这样数据在内存会越存越多，最终导致内存溢出。</p></li><li><p>页面回收恢复后，重复创建Fragment导致重复的缓存数据<br>通过<code>FragmentManager.beginTransaction.add.commit</code>添加<code>Framgnet</code>时，但页面回收恢复后，<code>FragmentManager</code>仍然会保留所有之前的<code>Fragment</code>，所以：<br>方式一：重复使用缓存的Fragment，不再重复创建<br>方式二：在页面回收恢复后，先删除之前的所有Fragment</p><p><img src="https://image.log1992.cn/uPic/image-20230920154344484.png"></p></li><li><p>ViewPager.setOffscreenPageLimit太大导致Fragment被杀死时缓存的数据过大</p><p>如果页面数据很大，我们也应该格外注意setOffscreenPageLimit，如果OffscreenPageLimit的数量过大，应该选择使用本地持久化等方式对页面数据进行缓存。</p></li></ol>]]>
      </content:encoded>
    </item>
    <item>
      <title>2024春节旅程</title>
      <link>https://log1992.cn/2024/02/18/life/2024%E6%98%A5%E8%8A%82%E6%97%85%E7%A8%8B/</link>
      <description>
        <![CDATA[<blockquote>
<p>又是一年春节旅途，越是成长，过年的快乐就越少了，成年人的春节，比上班还累。</p>
</blockquote>
<h2 id="节前回家"><a href="#节前回家" class="headerlink" title="节前回家"></a>节前回家</h2><p>​	2月7号晚上从武汉出发回孝感，中午的时候还刷到各个大桥上冰锥掉落，砸坏汽车的，还好晚上回去的时候]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/life/">生活</category>
      <category domain="https://log1992.cn/tags/%E6%97%85%E9%80%94/">旅途</category>
      <pubDate>Sun, 18 Feb 2024 01:24:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>又是一年春节旅途，越是成长，过年的快乐就越少了，成年人的春节，比上班还累。</p></blockquote><h2 id="节前回家"><a href="#节前回家" class="headerlink" title="节前回家"></a>节前回家</h2><p>​2月7号晚上从武汉出发回孝感，中午的时候还刷到各个大桥上冰锥掉落，砸坏汽车的，还好晚上回去的时候已经没有冰锥掉落了，不然就得走长江隧道了。我一般回家都是走硚孝高速，通车没多久，车流量不多，路也好走。不过前几天下的冻雨和雪，路上只清理了一股道，基本上超车不是很方便。回去之前随便在滴滴出行上接了个顺风车的单子，全程下来顺路72km,赚了132块，相当的划算。路过东山收费站的时候我下去了一下，因为这一段路程免费，所以每次回去5块6的过路费还是挺香的。</p><p><img src="https://image.log1992.cn/uPic/image-20240218144227396.png"></p><h2 id="拜年-生日"><a href="#拜年-生日" class="headerlink" title="拜年+生日"></a>拜年+生日</h2><p>​今年初三，先是去给几个姑妈拜年了，最远的也就是朱湖农场，我们早上8点出发，路上车并不多，返程的时候在路边买了两斤草莓，牌子上写着10元&#x2F;斤，下车一看是那种挑出来的小果子，正儿八经的果子25&#x2F;斤，挑好后又开始返程，马不停蹄的去了五个姑妈家，堪称打卡式拜年，总算是在12点前把年拜完了。然后就是去外婆家吃饭了。给外公外婆拜年后，我们就留在外婆家了，也没有啥其他爱好，麻将扑克也不喜欢玩，所以就和表弟两个人开启了双排俯冲之路。晚上一大家子人一起给外婆祝寿，吃蛋糕，拍了全家福，这一天是我过年这段时间最充实的一天了。</p><h2 id="回娘家"><a href="#回娘家" class="headerlink" title="回娘家"></a>回娘家</h2><p>​正月初五，带着老婆踏上了回娘家的路，出发之前给车充满了电，油也是回来的时候加满的，不过今年我选择走沪蓉高速转大广高速。初五路上的车不是很多，所以我这一路速度都不慢，平均车速都有100，总共350km的路程，早上9点钟出发，中午12点45到达。一个很明显的感受，在湖北段的时候，路两边都是生活垃圾，不知道为什么没有清理，虽然前几天确实因为冻雨很多人滞留在了湖北高速，但是初五的时候，天气晴朗，清理应该挺容易的。</p><p><img src="https://image.log1992.cn/uPic/image-20240218143422345.png"></p><p>​无意间看到这个新闻，不知道该如何评论，反正我到河南段后路上看不到垃圾。</p><p>怎么说呢，怒其不争吧！</p><p><img src="https://image.log1992.cn/uPic/image-20240218144157188.png"></p><h2 id="简单的快乐"><a href="#简单的快乐" class="headerlink" title="简单的快乐"></a>简单的快乐</h2><p>​过节回家，无非就是见想见的人，做没做的事儿。</p><p><img src="https://image.log1992.cn/uPic/20240214_174343.jpg"></p><p>一种童年搭台唱戏，庙会的感觉，有木有！</p><p>有一说一，淀粉肠真好吃。</p><h2 id="归程"><a href="#归程" class="headerlink" title="归程"></a>归程</h2><p>​正月初七，我们踏上了归程，假期余额已不足，满载着爹妈的爱，后备箱也塞的满满的。回来我们选择了走京港澳，其实是老婆喜欢走京港澳，因为河南段全是双向八车道，限速120，开起来是真的爽，但是这点快乐在接近九里关隧道的时候就满满无了，接下来就是无尽的堵车，幽灵堵车，同时我也很诧异，为什么湖北段会限速80，你没看错，限速80。两车道加上限速80 ，debuff叠满，下午一点多出发，6点40才到家，真的是把人都要堵疯了。</p><p><img src="https://image.log1992.cn/uPic/image-20240218144817085.png"></p><p>头一次发现高德地图还能看晚霞</p><p><img src="https://image.log1992.cn/uPic/20240216_182555.jpg"></p><p>贴一下回来和去的时候的表现能耗</p><p><img src="https://image.log1992.cn/uPic/20240217_115538.jpg"></p><p><img src="https://image.log1992.cn/uPic/20240214_124524.jpg"></p><p>电车果然是要跑得慢才能刷能耗，难怪平时在市区猛得很的电耗子，一到高速上就装起了孙子，不过对我来说，能踩到底就踩到底，油耗啥的，根本不在乎。</p><h2 id="返汉"><a href="#返汉" class="headerlink" title="返汉"></a>返汉</h2><p>​在家里修整了一晚上，初八中午吃过午饭，我们返回了武汉。顺路去首衡城，不愧是华中地区最大的水果、蔬菜、水产、冷链批发，真的很大。</p><p><img src="https://image.log1992.cn/uPic/20240217_121720.jpg"></p><p>东西也确实便宜，10斤的草莓这里75，其他的水果啥的就不说了。</p><p>最后又刷了一遍里程</p><p><img src="https://image.log1992.cn/uPic/20240217_132957.jpg"></p><p>天气好，电耗也低了。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>​假期结束了，越是年长，越是觉得假期匆忙。匆匆的来，匆匆的走。希望新的一年能够赚更多的钱，让身边的人过上更好地生活吧。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>打印PDF</title>
      <link>https://log1992.cn/2023/12/13/android/%E6%89%93%E5%8D%B0PDF/</link>
      <description>
        <![CDATA[<blockquote>
<p>需要将应用中生成的PDF直接打印出来</p>
</blockquote>
<h3 id="构建PDFDocumentAdapter"><a href="#构建PDFDocumentAdapter" class="headerlink" title="构建PDFDocumentAdapter"></a>构建PDFDocumentAdapter</h3><figure]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/Activity/">Activity</category>
      <category domain="https://log1992.cn/tags/%E6%89%93%E5%8D%B0/">打印</category>
      <pubDate>Wed, 13 Dec 2023 06:07:03 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>需要将应用中生成的PDF直接打印出来</p></blockquote><h3 id="构建PDFDocumentAdapter"><a href="#构建PDFDocumentAdapter" class="headerlink" title="构建PDFDocumentAdapter"></a>构建PDFDocumentAdapter</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PDFDocumentAdapter</span> <span class="keyword">extends</span> <span class="title class_">PrintDocumentAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">TAG</span> <span class="operator">=</span> <span class="string">&quot;PDFDocumentAdapter&quot;</span>;</span><br><span class="line">    Context context;</span><br><span class="line">    String pdfPath;</span><br><span class="line">    File pdfFile;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PDFDocumentAdapter</span><span class="params">(Context context, String pdfPath)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.context = context;</span><br><span class="line">        <span class="built_in">this</span>.pdfPath = pdfPath;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PDFDocumentAdapter</span><span class="params">(Context context, File pdfFile)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.context = context;</span><br><span class="line">        <span class="built_in">this</span>.pdfFile = pdfFile;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onLayout</span><span class="params">(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (cancellationSignal.isCanceled()) &#123;</span><br><span class="line">            callback.onLayoutCancelled();</span><br><span class="line">            InsToastUtil.showInBottom(<span class="string">&quot;取消打印&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            PrintDocumentInfo.<span class="type">Builder</span> <span class="variable">builder</span> <span class="operator">=</span></span><br><span class="line">                    <span class="keyword">new</span> <span class="title class_">PrintDocumentInfo</span>.Builder(<span class="string">&quot; file name&quot;</span>);</span><br><span class="line">            builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)</span><br><span class="line">                    .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)</span><br><span class="line">                    .build();</span><br><span class="line">            callback.onLayoutFinished(builder.build(),</span><br><span class="line">                    !newAttributes.equals(oldAttributes));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onWrite</span><span class="params">(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback)</span> &#123;</span><br><span class="line">        <span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">OutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            File file;</span><br><span class="line">            <span class="keyword">if</span> (pdfFile != <span class="literal">null</span>) &#123;</span><br><span class="line">                file = pdfFile;</span><br><span class="line">            &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                file = <span class="keyword">new</span> <span class="title class_">File</span>(pdfPath);</span><br><span class="line">            &#125;</span><br><span class="line">            in = <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(file);</span><br><span class="line">            out = <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(destination.getFileDescriptor());</span><br><span class="line"></span><br><span class="line">            <span class="type">byte</span>[] buf = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">16384</span>];</span><br><span class="line">            <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> ((size = in.read(buf)) &gt;= <span class="number">0</span></span><br><span class="line">                    &amp;&amp; !cancellationSignal.isCanceled()) &#123;</span><br><span class="line">                out.write(buf, <span class="number">0</span>, size);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (cancellationSignal.isCanceled()) &#123;</span><br><span class="line">                callback.onWriteCancelled();</span><br><span class="line">                InsToastUtil.showInBottom(<span class="string">&quot;取消打印&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                callback.onWriteFinished(<span class="keyword">new</span> <span class="title class_">PageRange</span>[]&#123;PageRange.ALL_PAGES&#125;);</span><br><span class="line">                InsToastUtil.showInBottom(<span class="string">&quot;打印完成&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            callback.onWriteFailed(e.getMessage());</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (in != <span class="literal">null</span>) &#123;</span><br><span class="line">                    in.close();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (out != <span class="literal">null</span>) &#123;</span><br><span class="line">                    out.close();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="打印"><a href="#打印" class="headerlink" title="打印"></a>打印</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 调用系统打印功能打印PDF</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">PrintManager</span> <span class="variable">printManager</span> <span class="operator">=</span> (PrintManager) getSystemService(Context.PRINT_SERVICE);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">PrintDocumentAdapter</span> <span class="variable">printAdapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PDFDocumentAdapter</span>(<span class="built_in">this</span>, Static.getRootFolderPath() + File.separator + <span class="string">&quot;test.pdf&quot;</span>);</span><br><span class="line">            printManager.print(<span class="string">&quot;Document&quot;</span>, printAdapter, <span class="keyword">new</span> <span class="title class_">PrintAttributes</span>.Builder().build());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p><img src="https://image.log1992.cn/uPic/Xnip2023-12-13_14-15-23.png"></p><p>需要手动添加打印机，即可打印，但是打印过程中存在纸张尺寸不匹配问题</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Android 界面灰化处理</title>
      <link>https://log1992.cn/2023/07/26/android/Android%E7%95%8C%E9%9D%A2%E7%81%B0%E5%8C%96%E5%A4%84%E7%90%86/</link>
      <description>
        <![CDATA[<blockquote>
<p>在某些特定时刻，我们需要把app所有界面变为灰色，在web界面中我们可以使用<code>filter:gray</code>或者<code>-webkit-filter: grayscale(100%);</code>添加灰色滤镜，那么在app中该如何实现呢？</p>
</blockquote>
<h4 id="原理"><a href="#原理" class="he]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/%E4%B8%80%E9%94%AE%E7%81%B0%E5%8C%96/">一键灰化</category>
      <pubDate>Wed, 26 Jul 2023 06:50:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>在某些特定时刻，我们需要把app所有界面变为灰色，在web界面中我们可以使用<code>filter:gray</code>或者<code>-webkit-filter: grayscale(100%);</code>添加灰色滤镜，那么在app中该如何实现呢？</p></blockquote><h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>在APP中将界面变灰，需要创建一个饱和度为0的颜色过滤器（饱和度为0时，view就变成了黑白色），将这个颜色过滤器通过setLayerType方法设置到当前View上。（此方法会开启此View的硬件加速功能）</p><p>因为我们要实现的功能是全局灰度化，所以首先view的选型最好是一个根view，这样我们就不用在每个子view上重复操作了，其次我们还要适配Dialog和PopWindow等Window类型的组件,所以我们不能从Activity上动心思了，因为Activity本质也是一个Window，所以综上所述，我们将代码的执行时机选在了WindowManager的addView方法上面，因为不论是Activity的创建还是Dialog等组件的创建最终都会走到这个方法里面。<br>我们只要在WindowManager执行addView方法时进行hook，加入我们上面的代码逻辑即可。</p><h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><p>要进行hook操作，那么我们就要先确定hook点：</p><p>WindowManager是一个接口类，其实现类为WindowManagerImpl，而WindowManagerImpl中真正实现窗口操作的类为WindowManagerGlobal，是一个单例类，我们看一下其addView的实现：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addView</span><span class="params">(View view, ViewGroup.LayoutParams params,</span></span><br><span class="line"><span class="params">            Display display, Window parentWindow)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (view == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;view must not be null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (display == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;display must not be null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!(params <span class="keyword">instanceof</span> WindowManager.LayoutParams)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Params must be WindowManager.LayoutParams&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> WindowManager.<span class="type">LayoutParams</span> <span class="variable">wparams</span> <span class="operator">=</span> (WindowManager.LayoutParams) params;</span><br><span class="line">        <span class="keyword">if</span> (parentWindow != <span class="literal">null</span>) &#123;</span><br><span class="line">            parentWindow.adjustLayoutParamsForSubWindow(wparams);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// If there&#x27;s no parent, then hardware acceleration for this view is</span></span><br><span class="line">            <span class="comment">// set from the application&#x27;s hardware acceleration setting.</span></span><br><span class="line">            <span class="keyword">final</span> <span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> view.getContext();</span><br><span class="line">            <span class="keyword">if</span> (context != <span class="literal">null</span></span><br><span class="line">                    &amp;&amp; (context.getApplicationInfo().flags</span><br><span class="line">                            &amp; ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != <span class="number">0</span>) &#123;</span><br><span class="line">                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ViewRootImpl root;</span><br><span class="line">        <span class="type">View</span> <span class="variable">panelParentView</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">            <span class="comment">// Start watching for system property changes.</span></span><br><span class="line">            <span class="keyword">if</span> (mSystemPropertyUpdater == <span class="literal">null</span>) &#123;</span><br><span class="line">                mSystemPropertyUpdater = <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">                    <span class="meta">@Override</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                        <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">                            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> mRoots.size() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i) &#123;</span><br><span class="line">                                mRoots.get(i).loadSystemProperties();</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;;</span><br><span class="line">                SystemProperties.addChangeCallback(mSystemPropertyUpdater);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> findViewLocked(view, <span class="literal">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (mDyingViews.contains(view)) &#123;</span><br><span class="line">                    <span class="comment">// Don&#x27;t wait for MSG_DIE to make it&#x27;s way through root&#x27;s queue.</span></span><br><span class="line">                    mRoots.get(index).doDie();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;View &quot;</span> + view</span><br><span class="line">                            + <span class="string">&quot; has already been added to the window manager.&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// The previous removeView() had not completed executing. Now it has.</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If this is a panel window, then find the window it is being</span></span><br><span class="line">            <span class="comment">// attached to for future reference.</span></span><br><span class="line">            <span class="keyword">if</span> (wparams.type &gt;= WindowManager.LayoutParams.FIRST_SUB_WINDOW &amp;&amp;</span><br><span class="line">                    wparams.type &lt;= WindowManager.LayoutParams.LAST_SUB_WINDOW) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mViews.size();</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mRoots.get(i).mWindow.asBinder() == wparams.token) &#123;</span><br><span class="line">                        panelParentView = mViews.get(i);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            root = <span class="keyword">new</span> <span class="title class_">ViewRootImpl</span>(view.getContext(), display);</span><br><span class="line"></span><br><span class="line">            view.setLayoutParams(wparams);</span><br><span class="line"></span><br><span class="line">            mViews.add(view);</span><br><span class="line">            mRoots.add(root);</span><br><span class="line">            mParams.add(wparams);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// do this last because it fires off messages to start doing things</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                root.setView(view, wparams, panelParentView);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RuntimeException e) &#123;</span><br><span class="line">                <span class="comment">// BadTokenException or InvalidDisplayException, clean up.</span></span><br><span class="line">                <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    removeViewLocked(index, <span class="literal">true</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">throw</span> e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>我们最终是要操作view，可以看到所有的view都是添加到<code>mViews</code>这个字段中，那我们可以hook这个list，在其添加view的时候，我们就调用上面的代码将其置灰，那如何才能感知到view的添加操作呢？</p><ol><li><p>实现具有数据感知能力的list:</p><p>我们可以自定义一个数组，让其继承于 ArrayList，在其执行添加删除操作时，通过回调进行通知，具体实现如下：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ObservableArrayList</span>&lt;T&gt; <span class="keyword">extends</span> <span class="title class_">ArrayList</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;OnListChangeListener&gt; mListeners = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addOnListChangedListener</span><span class="params">(OnListChangeListener listener)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mListeners != <span class="literal">null</span>) &#123;</span><br><span class="line">            mListeners.add(listener);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeOnListChangedListener</span><span class="params">(OnListChangeListener listener)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mListeners != <span class="literal">null</span>) &#123;</span><br><span class="line">            mListeners.remove(listener);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">add</span><span class="params">(T object)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.add(object);</span><br><span class="line">        notifyAdd(size() - <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> index, T object)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.add(index, object);</span><br><span class="line">        notifyAdd(index, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">addAll</span><span class="params">(Collection&lt;? extends T&gt; collection)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">oldSize</span> <span class="operator">=</span> size();</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">added</span> <span class="operator">=</span> <span class="built_in">super</span>.addAll(collection);</span><br><span class="line">        <span class="keyword">if</span> (added) &#123;</span><br><span class="line">            notifyAdd(oldSize, size() - oldSize);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> added;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">oldSize</span> <span class="operator">=</span> size();</span><br><span class="line">        <span class="built_in">super</span>.clear();</span><br><span class="line">        <span class="keyword">if</span> (oldSize != <span class="number">0</span>) &#123;</span><br><span class="line">            notifyRemove(<span class="number">0</span>, oldSize);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">T</span> <span class="variable">val</span> <span class="operator">=</span> <span class="built_in">super</span>.remove(index);</span><br><span class="line">        notifyRemove(index, <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> val;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object object)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> indexOf(object);</span><br><span class="line">        <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            remove(index);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">set</span><span class="params">(<span class="type">int</span> index, T object)</span> &#123;</span><br><span class="line">        <span class="type">T</span> <span class="variable">val</span> <span class="operator">=</span> <span class="built_in">super</span>.set(index, object);</span><br><span class="line">        <span class="keyword">if</span> (mListeners != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (OnListChangeListener listener : mListeners) &#123;</span><br><span class="line">                listener.onChange(<span class="built_in">this</span>, index, <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> val;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">removeRange</span><span class="params">(<span class="type">int</span> fromIndex, <span class="type">int</span> toIndex)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.removeRange(fromIndex, toIndex);</span><br><span class="line">        notifyRemove(fromIndex, toIndex - fromIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">notifyAdd</span><span class="params">(<span class="type">int</span> start, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mListeners != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (OnListChangeListener listener : mListeners) &#123;</span><br><span class="line">                listener.onAdd(<span class="built_in">this</span>, start, count);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">notifyRemove</span><span class="params">(<span class="type">int</span> start, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mListeners != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (OnListChangeListener listener : mListeners) &#123;</span><br><span class="line">                listener.onRemove(<span class="built_in">this</span>, start, count);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OnListChangeListener</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">onChange</span><span class="params">(ArrayList list, <span class="type">int</span> index, <span class="type">int</span> count)</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">onAdd</span><span class="params">(ArrayList list, <span class="type">int</span> start, <span class="type">int</span> count)</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">onRemove</span><span class="params">(ArrayList list, <span class="type">int</span> start, <span class="type">int</span> count)</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>hook操作</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GlobalGray</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> enable 是否开启全局灰色调</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">enable</span><span class="params">(<span class="type">boolean</span> enable)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!enable) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//灰色调Paint</span></span><br><span class="line">            <span class="keyword">final</span> <span class="type">Paint</span> <span class="variable">mPaint</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Paint</span>();</span><br><span class="line">            <span class="type">ColorMatrix</span> <span class="variable">mColorMatrix</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ColorMatrix</span>();</span><br><span class="line">            mColorMatrix.setSaturation(<span class="number">0</span>);</span><br><span class="line">            mPaint.setColorFilter(<span class="keyword">new</span> <span class="title class_">ColorMatrixColorFilter</span>(mColorMatrix));</span><br><span class="line"></span><br><span class="line">            <span class="comment">//反射获取windowManagerGlobal</span></span><br><span class="line">            <span class="meta">@SuppressLint(&quot;PrivateApi&quot;)</span></span><br><span class="line">            Class&lt;?&gt; windowManagerGlobal = Class.forName(<span class="string">&quot;android.view.WindowManagerGlobal&quot;</span>);</span><br><span class="line">            <span class="meta">@SuppressLint(&quot;DiscouragedPrivateApi&quot;)</span></span><br><span class="line">            java.lang.reflect.<span class="type">Method</span> <span class="variable">getInstanceMethod</span> <span class="operator">=</span> windowManagerGlobal.getDeclaredMethod(<span class="string">&quot;getInstance&quot;</span>);</span><br><span class="line">            getInstanceMethod.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            <span class="type">Object</span> <span class="variable">windowManagerGlobalInstance</span> <span class="operator">=</span> getInstanceMethod.invoke(windowManagerGlobal);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//反射获取mViews</span></span><br><span class="line">            <span class="type">Field</span> <span class="variable">mViewsField</span> <span class="operator">=</span> windowManagerGlobal.getDeclaredField(<span class="string">&quot;mViews&quot;</span>);</span><br><span class="line">            mViewsField.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            <span class="type">Object</span> <span class="variable">mViewsObject</span> <span class="operator">=</span> mViewsField.get(windowManagerGlobalInstance);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//创建具有数据感知能力的ObservableArrayList</span></span><br><span class="line">            ObservableArrayList&lt;View&gt; observerArrayList = <span class="keyword">new</span> <span class="title class_">ObservableArrayList</span>&lt;&gt;();</span><br><span class="line">            observerArrayList.addOnListChangedListener(<span class="keyword">new</span> <span class="title class_">ObservableArrayList</span>.OnListChangeListener() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onChange</span><span class="params">(ArrayList list, <span class="type">int</span> index, <span class="type">int</span> count)</span> &#123;</span><br><span class="line"></span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onAdd</span><span class="params">(ArrayList list, <span class="type">int</span> start, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">                    <span class="type">View</span> <span class="variable">view</span> <span class="operator">=</span> (View) list.get(start);</span><br><span class="line">                    <span class="keyword">if</span> (view != <span class="literal">null</span>) &#123;</span><br><span class="line">                        view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onRemove</span><span class="params">(ArrayList list, <span class="type">int</span> start, <span class="type">int</span> count)</span> &#123;</span><br><span class="line"></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="comment">//将原有的数据添加到新创建的list</span></span><br><span class="line">            observerArrayList.addAll((ArrayList&lt;View&gt;) mViewsObject);</span><br><span class="line">            <span class="comment">//替换掉原有的mViews</span></span><br><span class="line">            mViewsField.set(windowManagerGlobalInstance, observerArrayList);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后，只需在Application的onCreate中调用上述代码进行hook即可。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">GlobalGray.enable(<span class="literal">true</span>);</span><br></pre></td></tr></table></figure></li></ol>]]>
      </content:encoded>
    </item>
    <item>
      <title>autMan命令汇总</title>
      <link>https://log1992.cn/2023/06/09/play/autMan%E5%91%BD%E4%BB%A4%E6%B1%87%E6%80%BB/</link>
      <description>
        <![CDATA[<p>autMan（奥特曼）机器人的常用配置命令汇总，涵盖基本设置、守护进程、对接 QQ &#x2F; TG &#x2F; 微信 &#x2F; 公众号、青龙、京东 cookie 与店铺签到等模块。</p>]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/Docker/">Docker</category>
      <category domain="https://log1992.cn/tags/autMan/">autMan</category>
      <pubDate>Fri, 09 Jun 2023 02:29:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>autMan（奥特曼）机器人的常用配置命令汇总，涵盖基本设置、守护进程、对接 QQ &#x2F; TG &#x2F; 微信 &#x2F; 公众号、青龙、京东 cookie 与店铺签到等模块。</p><span id="more"></span><h4 id="基本配置命令"><a href="#基本配置命令" class="headerlink" title="基本配置命令"></a>基本配置命令</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">【基本设置】</span><br><span class="line">set autMan name 奥特曼 <span class="params">#</span>设置奥特曼机器人名</span><br><span class="line">set autMan port 8080 <span class="params">#</span>设置奥特曼http服务端口</span><br><span class="line">set autMan duration 5 <span class="params">#</span>奥特曼消息撤回等待时间，单位秒</span><br><span class="line">set autMan update<span class="built_in">_</span>notify false <span class="params">#</span>奥特曼自动升级是否通知</span><br><span class="line">set autMan auto<span class="built_in">_</span>update true <span class="params">#</span>是否开启奥特曼自动更新</span><br><span class="line">set fanli appreciate https://xxxxxxx <span class="params">#</span>设置赞赏码,后面我图片的网址</span><br><span class="line">set autMan enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>是否启动http服务，对接微信公众号等需要，建议开启</span><br><span class="line">set autMan ignore<span class="built_in">_</span>notify true <span class="params">#</span> 奥特曼忽略通知命令，默认忽略。</span><br><span class="line">set qq spy<span class="built_in">_</span>on ? <span class="params">#</span> 返利间谍模式</span><br><span class="line">set autMan pushplus [token] <span class="params">#</span> 管理员推送绑定pushplus指令</span><br><span class="line">set autMan recall 关键词1<span class="built_in">&amp;</span>关键词2... <span class="params">#</span> 关键词撤回功能<span class="params">#</span>关键词支持正则表达式。</span><br><span class="line">set reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">delete reply ? <span class="params">#</span>删除用set设置的关键词回复</span><br><span class="line">reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">reply ? nil <span class="params">##</span>删除reply设置的关键词回复，请区别于用set reply设置关键词的删除方法。</span><br><span class="line">set autMan recall [<span class="keyword">\s</span><span class="keyword">\S</span>]*[<span class="built_in">^</span>0-9a-zA-Z=][0-9a-zA-Z]&#123;14&#125;[<span class="built_in">^</span>0-9a-zA-Z;][<span class="keyword">\s</span><span class="keyword">\S</span>]* <span class="params">#</span> 屏蔽京东口令小妙招</span><br><span class="line">重启 <span class="params">#</span>重启并静默运行</span><br><span class="line">命令 <span class="params">#</span>获取奥特曼的命令列表</span><br><span class="line"></span><br><span class="line">【守护奥特曼】 <span class="params">#</span>解决开机自启和崩溃重启</span><br><span class="line">systemctl disable autMan <span class="params">#</span>linux命令，关闭守护模式，守护模式会导致没法自动升级</span><br><span class="line">systemctl stop autMan <span class="built_in">&amp;</span><span class="built_in">&amp;</span> systemctl disable autMan <span class="params">#</span>linux命令，遇见奥特曼被被杀死，请执行命令</span><br><span class="line">docker-compose up -d --build <span class="params">#</span>linux命令，docker运行</span><br><span class="line"></span><br><span class="line">【对接qq】</span><br><span class="line">set qq tempMessageGroupCode ? <span class="params">#</span>设置qq临时消息默认群号</span><br><span class="line">set qq onGroups g1<span class="built_in">&amp;</span>g2<span class="built_in">&amp;</span>g3... <span class="params">#</span>指定要监听的qq群</span><br><span class="line">set qq auto<span class="built_in">_</span>friend false <span class="params">#</span>设置是否自动同意好友请求,似乎没用，不用在意。</span><br><span class="line">set qq onself true <span class="params">#</span>设置是否对监听自身消息</span><br><span class="line">set qq default<span class="built_in">_</span>bot 主机器人账号 <span class="params">#</span>奥特曼支持对接多个qq，设置主qq机器人</span><br><span class="line">set qq masters q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置qq管理员</span><br><span class="line">set qq notifier q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置接受通知的qq账号</span><br><span class="line">list qq  查询qq下的各类参数设置</span><br><span class="line"></span><br><span class="line">【对接tgbot】tg机器人</span><br><span class="line">set tb token ? <span class="params">#</span>设置telegram机器人token</span><br><span class="line">set tb http<span class="built_in">_</span>proxy ? <span class="params">#</span>设置telegram机器人代理</span><br><span class="line">set tb masters t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... <span class="params">#</span>设置telegram机器人管理员</span><br><span class="line">set tb notifier t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... 设置接受通知的telegram账号</span><br><span class="line">list tb  查询tgbot下的各类参数设置</span><br><span class="line"></span><br><span class="line">【对接tdlib】tg客户端，tdlib，与终端端APP不冲突</span><br><span class="line">set tg auto<span class="built_in">_</span>login true/false <span class="params">#</span>设置是否自动登陆tg客户端，需手工完成第一次登陆过程。</span><br><span class="line">set tg app<span class="built_in">_</span>id xxx <span class="params">#</span>设置app<span class="built_in">_</span>id</span><br><span class="line">set tg app<span class="built_in">_</span>hash xxxx <span class="params">#</span>设置app<span class="built_in">_</span>hash</span><br><span class="line">set tg masters t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... <span class="params">#</span>设置tg管理员</span><br><span class="line">set tg replyGroups t1,t2,t3... <span class="params">#</span>设置开放回复的群号</span><br><span class="line"></span><br><span class="line">【对接微信公众号】</span><br><span class="line">set wxmp app<span class="built_in">_</span>id ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>id</span><br><span class="line">set wxmp app<span class="built_in">_</span>secret ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>secret </span><br><span class="line">set wxmp token ? <span class="params">#</span>设置微信公众平台token</span><br><span class="line">set wxmp encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key ? <span class="params">#</span>设置微信公众平台encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key</span><br><span class="line">set wxmp masters w1<span class="built_in">&amp;</span>w2<span class="built_in">&amp;</span>w3 <span class="params">#</span>设置微信公众平台管理员</span><br><span class="line">set wxmp subscribe<span class="built_in">_</span>reply ? <span class="params">#</span>设置公众号关注事件回复</span><br><span class="line">set wxmp default<span class="built_in">_</span>reply 无法回复该消息 设置公众号默认回复</span><br><span class="line"></span><br><span class="line">【对接微信-可爱猫】</span><br><span class="line">set wx api<span class="built_in">_</span>url ? <span class="params">#</span>设置插件调用地址，确保奥特曼可以访问可爱猫端口，http://可爱猫地址:可爱猫端口/wx/receive</span><br><span class="line">set wx keaimao<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置可爱猫是否动态网络地址，适用于可爱猫家庭宽带而奥特曼在云服务器的情况下</span><br><span class="line">set wx keaimao<span class="built_in">_</span>port ? <span class="params">#</span>设置可爱猫端口</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，不知道不用填</span></span><br><span class="line">set wx autMan<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置奥特曼是否动态网络地址，适用于奥特曼家庭宽带而可爱猫在云服务器的情况下</span><br><span class="line">set autMan enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br><span class="line">list wx 查询wx下的各类参数设置</span><br><span class="line"></span><br><span class="line">【对接微信-vlw/我的/西瓜】</span><br><span class="line">set wx vlw<span class="built_in">_</span>addr http://vlw插件ip:端口 <span class="params">#</span>设置插件调用地址，对应之前插件配置的 序号3 HTTP 外网API调用地址</span><br><span class="line">set wx vlw<span class="built_in">_</span>token XXX <span class="params">#</span>设置对接vlw插件的token，对应之前插件配置的 序号2 API调用Token，例如autMan</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示。不懂就不要设置了。</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，这个我也不知道干嘛的，不知道就别设置了。</span></span><br><span class="line">set autMan enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br><span class="line"></span><br><span class="line">【对接微信-鲲鹏】</span><br><span class="line">set wx kp<span class="built_in">_</span>addr http://xxx.xxx.xxx.xxx:2022  <span class="params">#</span>设置地址</span><br><span class="line">set wx mode kp <span class="params">#</span>设置模式</span><br><span class="line">鲲鹏使用httpApi插件设置如下：</span><br><span class="line">消息推送：http://autMan的地址:autMan端口/wx/receive</span><br><span class="line">外网服务：开启</span><br><span class="line">开关服务：开启</span><br><span class="line">鉴权开关不要打开，暂时没有写鉴权程序</span><br><span class="line"></span><br><span class="line">【对接青龙】</span><br><span class="line">青龙管理 <span class="params">#</span>多容器青龙配置</span><br><span class="line"><span class="params">#</span> 使用命令</span><br><span class="line">ql config <span class="params">#</span>获取青龙config.sh设置的内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql envs <span class="params">#</span>获取青龙config.sh内设置的所有环境变量内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env get ? <span class="params">#</span>获取青龙config.sh内指定环境变量的内容，仅精确匹配</span><br><span class="line">ql env find ? <span class="params">#</span>查找青龙config.sh内指定的环境变量内容，支持模糊匹配</span><br><span class="line">ql env set ? ? <span class="params">#</span>在青龙config.sh内设置环境变量</span><br><span class="line">ql env remark ? ? <span class="params">#</span>字面意思是给环境变量设置备注，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env disable ? <span class="params">#</span>禁用（注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql env enable ? <span class="params">#</span>启用（取消注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql raw ? <span class="params">#</span>下载raw链接的js</span><br><span class="line">ql task ? <span class="params">#</span>在青龙里面运行指定的js</span><br><span class="line">ql repo ? <span class="params">#</span>在青龙里面拉库，例：ql repo https://github.com/cdle/carry.git</span><br><span class="line">ql cookie status <span class="params">#</span>查询青龙里cookie状态</span><br><span class="line">ql crons <span class="params">#</span>获取青龙所有定时任务，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql cron status ? <span class="params">#</span>查看青龙指定定时任务的状态</span><br><span class="line">ql cron run ? <span class="params">#</span>运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron stop ? <span class="params">#</span>停止运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron enable ? <span class="params">#</span>启用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron disable ? <span class="params">#</span>禁用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron find ? <span class="params">#</span>查找青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron logs ? <span class="params">#</span>查找青龙定时任务运行日志，支持模糊匹配</span><br><span class="line">ql cron hide duplicate <span class="params">#</span>隐藏青龙重复定时任务</span><br><span class="line">set qinglong autoCronHideDuplicate false <span class="params">#</span>关闭自动隐藏任务命令</span><br><span class="line"></span><br><span class="line">【短信登陆】</span><br><span class="line"><span class="params">#</span> 设置诺兰登陆地址</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>nark true</span><br><span class="line">set jd<span class="built_in">_</span>cookie nark<span class="built_in">_</span>addr http://ip:端口</span><br><span class="line"><span class="params">#</span> 设置兔子登陆地址</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>rabbit true</span><br><span class="line">set jd<span class="built_in">_</span>cookie rabbit<span class="built_in">_</span>addr http://ip:端口</span><br><span class="line"><span class="params">#</span> 设置</span><br><span class="line"><span class="params">#</span>使用命令：</span><br><span class="line">1、“登录”或“登陆”：登录某东</span><br><span class="line">2、“q“：退出登录</span><br><span class="line"></span><br><span class="line">【资产查询】</span><br><span class="line">set jd<span class="built_in">_</span>cookie jd<span class="built_in">_</span>ck<span class="built_in">_</span>unbind<span class="built_in">_</span>hint 未绑定jd账号 <span class="params">#</span>未绑定自定义回复</span><br><span class="line"></span><br><span class="line">【对接小爱同学】</span><br><span class="line"><span class="params">#</span>小爱同学，可以指定API，对小爱说对话模式即可开启连续对话模式，闭嘴可关闭。</span><br><span class="line">set autMan 小爱同学 http://81.70.100.130/api/xiaoai.php?msg=<span class="comment">%s&amp;n=text #挂了就自己换</span></span><br><span class="line">set autMan 小爱同学 http://jiuli.xiaoapi.cn/i/xiaoai<span class="built_in">_</span>tts.php?msg=<span class="comment">%s #挂了就自己换</span></span><br><span class="line">set autMan 小爱同学gjson text <span class="params">#</span> 处理json格式数据的小爱api，不填获取整个文本</span><br><span class="line">aut delete 小爱同学gjson <span class="params">#</span> 删除处理json格式数据的小爱api</span><br><span class="line"><span class="params">#</span> 使用方法：</span><br><span class="line">1、唤起小爱：发送“小爱+想说的话”，例如：小爱今天天气。</span><br><span class="line">2、对话模式：发送“小爱对话模式”开启，发送“闭嘴”结束。</span><br><span class="line">3、成语接龙：发送“成语接龙”开始。这个可应该不是小爱同学的功能，但我懒得单独写它了。</span><br><span class="line"></span><br><span class="line">【洞察变量】-填写说明</span><br><span class="line">任务开关：想开就开，想关就关</span><br><span class="line">静默运行：在别人的群里可能会需要这个功能。</span><br><span class="line">任务名称：自己随意起名字，自己好识别就行了。</span><br><span class="line">目的脚本：想运行哪个脚本，如：jd<span class="built_in">_</span>team60.js</span><br><span class="line">激活变量：哪个变量激活脚本如export ccc=&quot;123&quot;，ccc就是激活变量</span><br><span class="line">监听目标：把监听的群号放进去，多个用英文逗号隔开</span><br><span class="line">指定容器：设置的容器的名称，不要知道的用命令：青龙管理，多个用英文逗号隔开</span><br><span class="line"></span><br><span class="line">【洞察链接】</span><br><span class="line">set jd<span class="built_in">_</span>cookie hint<span class="built_in">_</span>add<span class="built_in">_</span>varlink false //关闭洞察链接的智能查找匹配脚本的提示</span><br><span class="line"></span><br><span class="line">【店铺签到】：</span><br><span class="line">指令：签到管理</span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>parallel true/false   <span class="params">#</span>是否开启多进程并行签到（可能会导致autMan重启，默认关闭）</span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>limit<span class="built_in">_</span>num 50  <span class="params">#</span>设置签到CK限制数量</span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>cron 5 5,14 * * * <span class="params">#</span>设置自动签到时间,默认是0点0分</span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>vess 青龙1,青龙2,青龙3  <span class="params">#</span>指定执行签到的容器，不指定则取默认容器</span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>notify true/false <span class="params">#</span>设置是否通知到个人</span><br><span class="line">提取token并自动过滤无效后执行签到并加入到token库(注：短链之间的链接符无所谓)</span><br><span class="line">1. tokens pick https://u.jd.com/xxxxxxx;https://u.jd.com/xxxxxxx </span><br><span class="line">2. tokens check 检查token并剔除无效</span><br><span class="line">3. tokens sign 手动执行签到任务</span><br><span class="line">添加新token并执行签到时识别以下格式(连接符可任意，这里以<span class="built_in">&amp;</span>为例):</span><br><span class="line">1. 从APP分享店铺首页到autMan</span><br><span class="line">2. TOKEN:xxxxxxxx<span class="built_in">&amp;</span>xxxxxxxx<span class="built_in">&amp;</span>xxxxxx</span><br><span class="line">3. DPQDTK=xxxxxxxxx<span class="built_in">&amp;</span>xxxxxxx<span class="built_in">&amp;</span>xxxxxxx</span><br><span class="line">4. &#123;&quot;token&quot;:&quot;xxxxxxxx&quot;&#125;</span><br><span class="line">5. 签到xx天得xx豆</span><br><span class="line">等</span><br><span class="line"></span><br><span class="line"><span class="params">#</span>jd口令解析对接</span><br><span class="line">set jd<span class="built_in">_</span>cookie jd<span class="built_in">_</span>cmd<span class="built_in">_</span>addr http://ikuai.zhelee.cn:9000 <span class="params">#</span>对接解析服务器</span><br><span class="line">set jd<span class="built_in">_</span>cookie jd<span class="built_in">_</span>cmd<span class="built_in">_</span>groups 123,456  <span class="params">#</span>指定开放解析的群</span><br><span class="line"><span class="params">#</span>对接诺兰公益解析接口</span><br><span class="line">set jd<span class="built_in">_</span>cookie jd<span class="built_in">_</span>cmd<span class="built_in">_</span>nolan<span class="built_in">_</span>addr https://api.nolanstore.top <span class="params">#</span>对接解析服务器</span><br><span class="line">set jd<span class="built_in">_</span>cookie jd<span class="built_in">_</span>cmd<span class="built_in">_</span>nolan<span class="built_in">_</span>groups 123,456  <span class="params">#</span>指定开放解析的群</span><br><span class="line"></span><br><span class="line">set jd<span class="built_in">_</span>cookie dpqd<span class="built_in">_</span>token<span class="built_in">_</span>api<span class="built_in">_</span>addr http://rpi.zhelee.cn:9999/ <span class="params">#</span>奥特曼对接可提取店铺签到token</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">内置外卖返利，前提条件是设置了订单侠平台的apikey账号</span><br><span class="line">set fanli enable<span class="built_in">_</span>ele true/false 开关饿了么外卖，默认开启</span><br><span class="line">set fanli enable<span class="built_in">_</span>mt true/false 开关美团外卖，默认开启</span><br><span class="line">set fanli ele<span class="built_in">_</span>cron 0 11,17 * * *设置饿了么推送时间</span><br><span class="line">set fanli mt<span class="built_in">_</span>cron 0 11,17 * * *    设置美团推送时间</span><br><span class="line">set fanli ele<span class="built_in">_</span>ad设置饿了么文案</span><br><span class="line">set fanli mt<span class="built_in">_</span>ad    设置美团文案</span><br><span class="line">set fanli ele<span class="built_in">_</span>to<span class="built_in">_</span>groups qqgroup:123,wxgroup:456, 设置饿了么推送群</span><br><span class="line">set fanli mt<span class="built_in">_</span>to<span class="built_in">_</span>groups qqgroup:123 设置美团推送群</span><br></pre></td></tr></table></figure><h4 id="与机器人交互"><a href="#与机器人交互" class="headerlink" title="与机器人交互"></a>与机器人交互</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 设置容器登录命令</span><br><span class="line">set jd<span class="built_in">_</span>cookie sms<span class="built_in">_</span>login<span class="built_in">_</span>alias xxx</span><br><span class="line"><span class="params">#</span> 开启群回复机器人</span><br><span class="line">打开群回复</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>青龙依赖和cron表达式</title>
      <link>https://log1992.cn/2023/06/06/play/%E9%9D%92%E9%BE%99%E4%BE%9D%E8%B5%96%E5%92%8Ccron%E8%A1%A8%E8%BE%BE%E5%BC%8F/</link>
      <description>
        <![CDATA[<blockquote>
<p>青龙面板跑脚本离不开依赖和定时规则，这篇汇总一键安装依赖的命令和 cron 表达式的常用写法，备查。</p>
</blockquote>]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/Docker/">Docker</category>
      <category domain="https://log1992.cn/tags/%E9%9D%92%E9%BE%99%E9%9D%A2%E6%9D%BF/">青龙面板</category>
      <pubDate>Tue, 06 Jun 2023 03:40:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>青龙面板跑脚本离不开依赖和定时规则，这篇汇总一键安装依赖的命令和 cron 表达式的常用写法，备查。</p></blockquote><span id="more"></span><h4 id="自动安装"><a href="#自动安装" class="headerlink" title="自动安装"></a>自动安装</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">一键依赖</span><br><span class="line">docker exec -it qinglong bash</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 安装青龙依赖</span></span></span><br><span class="line">curl -fsSL https://ghproxy.com/https://raw.githubusercontent.com/FlechazoPh/QLDependency/main/Shell/QLOneKeyDependency.sh | sh</span><br></pre></td></tr></table></figure><h4 id="NodeJs-依赖"><a href="#NodeJs-依赖" class="headerlink" title="NodeJs 依赖"></a>NodeJs 依赖</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">json5</span><br><span class="line">js-base64</span><br><span class="line">require</span><br><span class="line">tough-cookie</span><br><span class="line">jsdom</span><br><span class="line">global-agent</span><br><span class="line">@types/node</span><br><span class="line">typescript</span><br><span class="line">dotenv</span><br><span class="line">jsdom -g</span><br><span class="line">form-data</span><br><span class="line">png-js</span><br><span class="line">ts-md5</span><br><span class="line">tslib</span><br><span class="line">jieba</span><br><span class="line">ws@7.4.3</span><br><span class="line">axios</span><br><span class="line">date-fns</span><br><span class="line">moment</span><br><span class="line">prettytable</span><br><span class="line">fs</span><br><span class="line">crypto-jsts-node</span><br><span class="line">depend</span><br><span class="line">ds</span><br><span class="line">jsdom</span><br><span class="line">requests</span><br><span class="line">npm</span><br><span class="line">node-jsencrypt</span><br><span class="line">-g npm</span><br><span class="line">cjs</span><br><span class="line">upgrade pip</span><br><span class="line">common</span><br><span class="line">qs</span><br><span class="line">ql</span><br><span class="line">node-telegram-bot-api</span><br><span class="line">crypto -g</span><br><span class="line">-g typescipt</span><br><span class="line">https</span><br><span class="line">proxy</span><br><span class="line">agent</span><br></pre></td></tr></table></figure><h4 id="python3-依赖"><a href="#python3-依赖" class="headerlink" title="python3 依赖"></a>python3 依赖</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">pycryptodome</span><br><span class="line">pytz</span><br><span class="line">typescript</span><br><span class="line">httpx</span><br><span class="line">success</span><br><span class="line">--upgrade pip</span><br><span class="line">pip</span><br><span class="line">redis</span><br><span class="line">jieba</span><br><span class="line">canvas</span><br><span class="line">requests</span><br><span class="line">ping3</span><br><span class="line">PyExecJS</span><br></pre></td></tr></table></figure><h4 id="Linux依赖（安装失败的忽略）"><a href="#Linux依赖（安装失败的忽略）" class="headerlink" title="Linux依赖（安装失败的忽略）"></a>Linux依赖（安装失败的忽略）</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">gcc</span><br><span class="line">g++</span><br><span class="line">python3-dev</span><br><span class="line">libc-dev</span><br></pre></td></tr></table></figure><h4 id="cron表达式"><a href="#cron表达式" class="headerlink" title="cron表达式"></a>cron表达式</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">(分 时 天 月 周)</span><br><span class="line">20 */12 * * * 每12小时的20分</span><br><span class="line">0 0,1-23/3 * * * 0点 1 点 后每3小时</span><br><span class="line">13 1,6,22 * * * 1:13 6:13 22:13运行</span><br><span class="line">0 20 30 * * 每月30日20点运行</span><br><span class="line">0 20 * * 7 每周日20点</span><br><span class="line">12 8 * * *每天8:12</span><br><span class="line">0 0-23/1 * * * 每小时一次</span><br><span class="line">58 0,9,19 * * * 0点58，09点58，19点58运行，每天3次</span><br><span class="line">0 8,14 * * * 每天8点，14点运行，每天2次</span><br><span class="line">*/15 6-23 * * * 每15分钟运行</span><br><span class="line">30 3-23/8 * * * 03.30，11.30，19.30运行，每8小时运行，每天3次</span><br><span class="line">0 1-23/4 * * * 每4小时运行，每天6次</span><br></pre></td></tr></table></figure><p>使用**<a href="https://www.iamwawa.cn/crontab.html">蛙蛙工具</a>**可以更直观的确定你想要的结果</p><p><img src="https://image.log1992.cn/uPic/image-20230613154226345.png"></p><h4 id="完整命令"><a href="#完整命令" class="headerlink" title="完整命令"></a>完整命令</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 基础配置</span><br><span class="line">set sillyGirl name 傻妞 <span class="params">#</span>设置傻妞机器人名</span><br><span class="line">set sillyGirl port 8080 <span class="params">#</span>设置傻妞http服务端口</span><br><span class="line">set sillyGirl duration 5 <span class="params">#</span>傻妞消息撤回等待时间，单位秒</span><br><span class="line">set sillyGirl update<span class="built_in">_</span>notify false <span class="params">#</span>傻妞自动升级是否通知</span><br><span class="line">set sillyGirl auto<span class="built_in">_</span>update true <span class="params">#</span>是否开启傻妞自动更新</span><br><span class="line">set reply 打赏 [CQ:image,file=https://xxxxxxx] <span class="params">#</span>傻妞内置赞赏码</span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>是否启动http服务，对接微信公众号等需要，建议开启</span><br><span class="line">set sillyGirl ignore<span class="built_in">_</span>notify true <span class="params">#</span> 傻妞忽略通知命令，默认忽略。</span><br><span class="line">set qq spy<span class="built_in">_</span>on ? <span class="params">#</span> 返利间谍模式</span><br><span class="line">set sillyGirl pushplus [token] <span class="params">#</span> 管理员推送绑定pushplus指令</span><br><span class="line">set sillyGirl recall 关键词1<span class="built_in">&amp;</span>关键词2... <span class="params">#</span> 关键词撤回功能<span class="params">#</span>关键词支持正则表达式。</span><br><span class="line">set reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">delete reply ? <span class="params">#</span>删除用set设置的关键词回复</span><br><span class="line">reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">reply ? nil <span class="params">##</span>删除reply设置的关键词回复，请区别于用set reply设置关键词的删除方法。</span><br><span class="line">set sillyGirl recall [<span class="keyword">\s</span><span class="keyword">\S</span>]*[<span class="built_in">^</span>0-9a-zA-Z=][0-9a-zA-Z]&#123;14&#125;[<span class="built_in">^</span>0-9a-zA-Z;][<span class="keyword">\s</span><span class="keyword">\S</span>]* <span class="params">#</span> 屏蔽京东口令小妙招</span><br><span class="line">重启 <span class="params">#</span>重启并静默运行</span><br><span class="line">命令 <span class="params">#</span>获取傻妞的命令列表</span><br><span class="line">守护傻妞 <span class="params">#</span>解决开机自启和崩溃重启</span><br><span class="line">systemctl disable sillyGirl <span class="params">#</span>linux命令，关闭守护模式，守护模式会导致没法自动升级</span><br><span class="line">systemctl stop sillyGirl <span class="built_in">&amp;</span><span class="built_in">&amp;</span> systemctl disable sillyGirl <span class="params">#</span>linux命令，遇见傻妞被被杀死，请执行命令</span><br><span class="line">docker-compose up -d --build <span class="params">#</span>linux命令，docker运行</span><br><span class="line"><span class="params">#</span> 对接qq</span><br><span class="line">set qq tempMessageGroupCode ? <span class="params">#</span>设置qq临时消息默认群号</span><br><span class="line">set qq onGroups g1<span class="built_in">&amp;</span>g2<span class="built_in">&amp;</span>g3... <span class="params">#</span>指定要监听的qq群</span><br><span class="line">set qq auto<span class="built_in">_</span>friend false <span class="params">#</span>设置是否自动同意好友请求,似乎没用，不用在意。</span><br><span class="line">set qq onself true <span class="params">#</span>设置是否对监听自身消息</span><br><span class="line">set qq default<span class="built_in">_</span>bot 主机器人账号 <span class="params">#</span>傻妞支持对接多个qq，设置主qq机器人</span><br><span class="line">set qq masters q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置qq管理员</span><br><span class="line">set qq notifier q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置接受通知的qq账号</span><br><span class="line"><span class="params">#</span> 对接telegtam</span><br><span class="line">set tg token ? <span class="params">#</span>设置telegram机器人token</span><br><span class="line">set tg http<span class="built_in">_</span>proxy ? <span class="params">#</span>设置telegram机器人代理</span><br><span class="line">set tg masters t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... <span class="params">#</span>设置telegram机器人管理员</span><br><span class="line">set tg notifier t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... 设置接受通知的telegram账号</span><br><span class="line"><span class="params">#</span> 对接微信公众号</span><br><span class="line">set wxmp app<span class="built_in">_</span>id ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>id</span><br><span class="line">set wxmp app<span class="built_in">_</span>secret ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>secret </span><br><span class="line">set wxmp token ? <span class="params">#</span>设置微信公众平台token</span><br><span class="line">set wxmp encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key ? <span class="params">#</span>设置微信公众平台encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key</span><br><span class="line">set wxmp masters w1<span class="built_in">&amp;</span>w2<span class="built_in">&amp;</span>w3 <span class="params">#</span>设置微信公众平台管理员</span><br><span class="line">set wxmp subscribe<span class="built_in">_</span>reply ? <span class="params">#</span>设置公众号关注事件回复</span><br><span class="line">set wxmp default<span class="built_in">_</span>reply 无法回复该消息 设置公众号默认回复</span><br><span class="line"><span class="params">#</span> 对接微信-可爱猫</span><br><span class="line">set wx api<span class="built_in">_</span>url ? <span class="params">#</span>设置插件调用地址，确保傻妞可以访问可爱猫端口</span><br><span class="line">set wx keaimao<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置可爱猫是否动态网络地址，适用于可爱猫家庭宽带而傻妞在云服务器的情况下</span><br><span class="line">set wx keaimao<span class="built_in">_</span>port ? <span class="params">#</span>设置可爱猫端口</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，不知道不用填</span></span><br><span class="line">set wx sillyGirl<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置傻妞是否动态网络地址，适用于傻妞家庭宽带而可爱猫在云服务器的情况下</span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br><span class="line"><span class="params">#</span> 对接微信-vlw</span><br><span class="line">set wx vlw<span class="built_in">_</span>addr http://vlw插件ip:端口 <span class="params">#</span>设置插件调用地址，对应之前插件配置的 序号3 HTTP 外网API调用地址</span><br><span class="line">set wx vlw<span class="built_in">_</span>token XXX <span class="params">#</span>设置对接vlw插件的token，对应之前插件配置的 序号2 API调用Token，例如sillyGirl</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示。不懂就不要设置了。</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，这个我也不知道干嘛的，不知道就别设置了。</span></span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br><span class="line"><span class="params">#</span> 对接青龙</span><br><span class="line">青龙管理 <span class="params">#</span>多容器青龙配置</span><br><span class="line">ql spy <span class="params">#</span>自定义监听变量运行青龙指定脚本功能</span><br><span class="line"><span class="params">#</span> 使用命令</span><br><span class="line">ql config <span class="params">#</span>获取青龙config.sh设置的内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql envs <span class="params">#</span>获取青龙config.sh内设置的所有环境变量内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env get ? <span class="params">#</span>获取青龙config.sh内指定环境变量的内容，仅精确匹配</span><br><span class="line">ql env find ? <span class="params">#</span>查找青龙config.sh内指定的环境变量内容，支持模糊匹配</span><br><span class="line">ql env set ? ? <span class="params">#</span>在青龙config.sh内设置环境变量</span><br><span class="line">ql env remark ? ? <span class="params">#</span>字面意思是给环境变量设置备注，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env disable ? <span class="params">#</span>禁用（注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql env enable ? <span class="params">#</span>启用（取消注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql raw ? <span class="params">#</span>下载raw链接的js</span><br><span class="line">ql task ? <span class="params">#</span>在青龙里面运行指定的js</span><br><span class="line">ql repo ? <span class="params">#</span>在青龙里面拉库，例：ql repo https://github.com/cdle/carry.git</span><br><span class="line">ql cookie status <span class="params">#</span>查询青龙里cookie状态</span><br><span class="line">ql crons <span class="params">#</span>获取青龙所有定时任务，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql cron status ? <span class="params">#</span>查看青龙指定定时任务的状态</span><br><span class="line">ql cron run ? <span class="params">#</span>运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron stop ? <span class="params">#</span>停止运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron enable ? <span class="params">#</span>启用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron disable ? <span class="params">#</span>禁用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron find ? <span class="params">#</span>查找青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron logs ? <span class="params">#</span>查找青龙定时任务运行日志，支持模糊匹配</span><br><span class="line">ql cron hide duplicate <span class="params">#</span>隐藏青龙重复定时任务</span><br><span class="line">set qinglong autoCronHideDuplicate false <span class="params">#</span>关闭自动隐藏任务命令</span><br><span class="line"><span class="params">#</span> 对接芝士</span><br><span class="line">h <span class="params">#</span>短信登录</span><br><span class="line">q <span class="params">#</span>退出短信登录</span><br><span class="line">登录 <span class="params">#</span>短信登录</span><br><span class="line">登陆 <span class="params">#</span>短信登录</span><br><span class="line">查询 ? <span class="params">#</span>查询指定账号的资产</span><br><span class="line">资产推送 <span class="params">#</span>向所有绑定了账号的用户推送资产</span><br><span class="line">查询 <span class="params">#</span>查询当前社交账号绑定的所有账号资产</span><br><span class="line">关闭?通知 <span class="params">#</span>关闭指定活动任务通知</span><br><span class="line">任务通知 <span class="params">#</span>推送账号失效、果园和萌宠成熟以及未继续种植通知。有私聊和群聊@两种方式，默认私聊。</span><br><span class="line">账号管理 <span class="params">#</span>自定义任务通知等</span><br><span class="line">推送管理 <span class="params">#</span>用户设置推送</span><br><span class="line">jd asset ? <span class="params">#</span>查询指定账号的资产</span><br><span class="line">jd imOf ? <span class="params">#</span>获取绑定的社交账号，仅精确匹配pt<span class="built_in">_</span>pin值</span><br><span class="line">jd find ? <span class="params">#</span>查找对应账号的编号、pt<span class="built_in">_</span>pin值、备注等信息，支持编号、pt<span class="built_in">_</span>pin值、备注查询，支持连号查询</span><br><span class="line">jd exchange ? ? <span class="params">#</span>交换两个账号的序号位置</span><br><span class="line">jd enable ? <span class="params">#</span>启用指定账号</span><br><span class="line">jd disable ? <span class="params">#</span>禁用指定账号</span><br><span class="line">jd remark ? ? <span class="params">#</span>备注指定账号</span><br><span class="line">jd remove ? <span class="params">#</span>跨容器删除ck，?可以匹配整个ck和相应的备注。芝士只有这一种方式删除ck</span><br><span class="line">jd send ? ? <span class="params">#</span>给指定账号发送消息</span><br><span class="line">jd unbind <span class="params">#</span>解绑该社交账号下的某栋账号</span><br><span class="line">jd check ? ? <span class="params">#</span>不知道干嘛的，第一个问号是pin值，第二个问号是青龙clientid</span><br><span class="line">jd myCookie <span class="params">#</span>查询绑定ck</span><br><span class="line">pt<span class="built_in">_</span>key=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+); pt<span class="built_in">_</span>pin=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+) <span class="params">#</span>发送ck提交到青龙</span><br><span class="line">pin=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+); wskey=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+) <span class="params">#</span>发送wskey提交到青龙</span><br><span class="line">packetId=?(<span class="built_in">&amp;</span>|<span class="built_in">&amp;</span>amp;)currentActId <span class="params">#</span>极速推一推助力</span><br><span class="line">set jd<span class="built_in">_</span>cookie notify<span class="built_in">_</span>mode group <span class="params">#</span>任务通知设置群聊模式</span><br><span class="line">set jd<span class="built_in">_</span>cookie qqGroup ? <span class="params">#</span>任务通知设置qq群聊ID</span><br><span class="line">set jd<span class="built_in">_</span>cookie wxGroup ? <span class="params">#</span>任务通知设置微信群聊ID</span><br><span class="line">set pinQQ pt<span class="built_in">_</span>pin qq号码 <span class="params">#</span>ck账号绑定qq号码，例 set pinQQ jd<span class="built_in">_</span>xxxxxx 123456</span><br><span class="line">delete pinQQ pt<span class="built_in">_</span>pin，<span class="params">#</span>取消ck账号绑定qq号码，例 delete pinQQ jd<span class="built_in">_</span>xxxxxx</span><br><span class="line">set pinTG pt<span class="built_in">_</span>pin TGID <span class="params">#</span>ck账号绑定TG账号，TGID找getmyid获取</span><br><span class="line">delete pinTG pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定TG账号</span><br><span class="line">set pinWX pt<span class="built_in">_</span>pin 微信号 <span class="params">#</span>微信给傻妞bot发送myuid获取，其实就是你的微信号，不是微信昵称。</span><br><span class="line">delete pinWX pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定微信账号</span><br><span class="line">set pinWXMP pt<span class="built_in">_</span>pin <span class="params">#</span>微信公众号用户id <span class="params">#</span>给公众号发送myuid获取</span><br><span class="line">delete pinWXMP pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定微信公众号用户号</span><br><span class="line">set jdWSCK update 56 * * * * <span class="params">#</span>设置wskey自动转cooke定时：</span><br><span class="line">set jd<span class="built_in">_</span>cookie query<span class="built_in">_</span>wait<span class="built_in">_</span>time [限制秒数] <span class="params">#</span>限制查询频率 例:set jd<span class="built_in">_</span>cookie query<span class="built_in">_</span>wait<span class="built_in">_</span>time 60</span><br><span class="line">set jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr 阿东ip:端口 <span class="params">#</span>设置阿东登录地址，不需要http，仅支持阿东1.7及以下版本</span><br><span class="line">set jd<span class="built_in">_</span>cookie selfQid 机器人qq账号 <span class="params">#</span>设置阿东qq机器人账号，仅支持阿东1.7及以下版本</span><br><span class="line">set jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr http://诺兰ip:端口 <span class="params">#</span>设置诺兰登陆地址，需要http</span><br><span class="line">set jd<span class="built_in">_</span>cookie login<span class="built_in">_</span>num ? <span class="params">#</span>设置登录坑位</span><br><span class="line">delete jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr 例：delete jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr ip:5701 <span class="params">#</span>删除阿东地址</span><br><span class="line">delete jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr 例：delete jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr http://ip:5701 <span class="params">#</span>删除nolan地址</span><br><span class="line">set jd<span class="built_in">_</span>cookie xdd<span class="built_in">_</span>url ?  <span class="params">#</span>短信登录接入xdd指令，格式http://IP地址:端口/api/login/smslogin</span><br><span class="line">set jd<span class="built_in">_</span>cookie xdd<span class="built_in">_</span>token ? <span class="params">#</span>对接xdd，额外设置参数</span><br><span class="line">set jd<span class="built_in">_</span>cookie asset<span class="built_in">_</span>push ? <span class="params">#</span>设置定时推送</span><br><span class="line">set jd<span class="built_in">_</span>cookie ad ? <span class="params">#</span>自定义广告，成功登录后发送</span><br><span class="line">set jd<span class="built_in">_</span>cookie asset<span class="built_in">_</span>query<span class="built_in">_</span>alias xxxxxxx <span class="params">#</span>自定义查询口令，变相实现屏蔽查询口令。</span><br><span class="line">set jd<span class="built_in">_</span>cookie disable<span class="built_in">_</span>notify true <span class="params">#</span>关闭推送指令，不想收到请jd unbind</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>auto<span class="built_in">_</span>update true <span class="params">#</span>自动检测ck有效性开关，关闭则设置为false。</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>yad false <span class="params">#</span>跳过云上阿东。</span><br><span class="line">set jd<span class="built_in">_</span>cookie login<span class="built_in">_</span>tip xxx <span class="params">#</span>你也可以自定义登录提示</span><br><span class="line">set jd<span class="built_in">_</span>cookie sms<span class="built_in">_</span>tip xxx <span class="params">#</span>接收短信验证码提示</span><br><span class="line">set silly http<span class="built_in">_</span>addr http://192.168.31.233:8080 <span class="params">#</span>失效ck无法禁用的，检查在青龙自动生成的傻妞地址GOTIFY<span class="built_in">_</span>URL，特定网络环境手动设置</span><br><span class="line"><span class="params">#</span> 对接小爱同学</span><br><span class="line"><span class="params">#</span>小爱同学，可以指定API，对小爱说对话模式即可开启连续对话模式，闭嘴可关闭。</span><br><span class="line">set sillyGirl 小爱同学 http://81.70.100.130/api/xiaoai.php?msg=<span class="comment">%s&amp;n=text #挂了就自己换</span></span><br><span class="line">set sillyGirl 小爱同学 http://jiuli.xiaoapi.cn/i/xiaoai<span class="built_in">_</span>tts.php?msg=<span class="comment">%s #挂了就自己换</span></span><br><span class="line">set sillyGirl 小爱同学gjson text <span class="params">#</span> 处理json格式数据的小爱api，不填获取整个文本</span><br><span class="line">silly delete 小爱同学gjson <span class="params">#</span> 删除处理json格式数据的小爱api</span><br><span class="line"><span class="params">#</span> 使用方法：</span><br><span class="line">1、唤起小爱：发送“小爱+想说的话”，例如：小爱今天天气。</span><br><span class="line">2、对话模式：发送“小爱对话模式”开启，发送“闭嘴”结束。</span><br><span class="line">3、成语接龙：发送“成语接龙”开始。这个可应该不是小爱同学的功能，但我懒得单独写它了。</span><br><span class="line"><span class="params">#</span> 对接短网址</span><br><span class="line">set dwz address ? <span class="params">#</span>设置短网址服务地址，填傻妞对外的可以访问的地址。</span><br><span class="line">set dwz prefix d <span class="params">#</span>设置短网址服务前缀</span><br><span class="line"><span class="params">#</span> 使用方法</span><br><span class="line">发送“短网址+你想要转换的网址” <span class="params">#</span>例：短网址 http://kejiwanjia.com/</span><br><span class="line"><span class="params">#</span> 对接返利</span><br><span class="line">fanli<span class="built_in">_</span>vip <span class="params">#</span>vip用户对接</span><br><span class="line">fanli<span class="built_in">_</span>edit <span class="params">#</span>进入返利傻瓜配置</span><br><span class="line">返利设置 <span class="params">#</span>进入返利傻瓜配置</span><br><span class="line">返利配置 <span class="params">#</span>进入返利傻瓜配置</span><br></pre></td></tr></table></figure><h4 id="基础配置"><a href="#基础配置" class="headerlink" title="基础配置"></a>基础配置</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 基础设置</span><br><span class="line">set sillyGirl name 傻妞 <span class="params">#</span>设置傻妞机器人名</span><br><span class="line">set sillyGirl port 8080 <span class="params">#</span>设置傻妞http服务端口</span><br><span class="line">set sillyGirl duration 5 <span class="params">#</span>傻妞消息撤回等待时间，单位秒</span><br><span class="line">set sillyGirl update<span class="built_in">_</span>notify false <span class="params">#</span>傻妞自动升级是否通知</span><br><span class="line">set sillyGirl auto<span class="built_in">_</span>update true <span class="params">#</span>是否开启傻妞自动更新</span><br><span class="line">set reply 打赏 [CQ:image,file=https://xxxxxxx] <span class="params">#</span>傻妞内置赞赏码</span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>是否启动http服务，对接微信公众号等需要，建议开启</span><br><span class="line">set sillyGirl ignore<span class="built_in">_</span>notify true <span class="params">#</span> 傻妞忽略通知命令，默认忽略。</span><br><span class="line">set qq spy<span class="built_in">_</span>on ? <span class="params">#</span> 返利间谍模式</span><br><span class="line">set sillyGirl pushplus [token] <span class="params">#</span> 管理员推送绑定pushplus指令</span><br><span class="line">set sillyGirl recall 关键词1<span class="built_in">&amp;</span>关键词2... <span class="params">#</span> 关键词撤回功能<span class="params">#</span>关键词支持正则表达式。</span><br><span class="line">set reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">delete reply ? <span class="params">#</span>删除用set设置的关键词回复</span><br><span class="line">reply ? ? <span class="params">##</span> 关键词回复功能 第一个问号是支持正则的关键词，第二个问号是回复的内容。</span><br><span class="line">reply ? nil <span class="params">##</span>删除reply设置的关键词回复，请区别于用set reply设置关键词的删除方法。</span><br><span class="line">set sillyGirl recall [<span class="keyword">\s</span><span class="keyword">\S</span>]*[<span class="built_in">^</span>0-9a-zA-Z=][0-9a-zA-Z]&#123;14&#125;[<span class="built_in">^</span>0-9a-zA-Z;][<span class="keyword">\s</span><span class="keyword">\S</span>]* <span class="params">#</span> 屏蔽京东口令小妙招</span><br><span class="line">重启 <span class="params">#</span>重启并静默运行</span><br><span class="line">命令 <span class="params">#</span>获取傻妞的命令列表</span><br><span class="line">守护傻妞 <span class="params">#</span>解决开机自启和崩溃重启</span><br><span class="line">systemctl disable sillyGirl <span class="params">#</span>linux命令，关闭守护模式，守护模式会导致没法自动升级</span><br><span class="line">systemctl stop sillyGirl <span class="built_in">&amp;</span><span class="built_in">&amp;</span> systemctl disable sillyGirl <span class="params">#</span>linux命令，遇见傻妞被被杀死，请执行命令</span><br><span class="line">docker-compose up -d --build <span class="params">#</span>linux命令，docker运行</span><br></pre></td></tr></table></figure><h4 id="对接qq、tg、微信公众号、微信等"><a href="#对接qq、tg、微信公众号、微信等" class="headerlink" title="对接qq、tg、微信公众号、微信等"></a>对接qq、tg、微信公众号、微信等</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 对接qq</span><br><span class="line">set qq tempMessageGroupCode ? <span class="params">#</span>设置qq临时消息默认群号</span><br><span class="line">set qq onGroups g1<span class="built_in">&amp;</span>g2<span class="built_in">&amp;</span>g3... <span class="params">#</span>指定要监听的qq群</span><br><span class="line">set qq auto<span class="built_in">_</span>friend false <span class="params">#</span>设置是否自动同意好友请求,似乎没用，不用在意。</span><br><span class="line">set qq onself true <span class="params">#</span>设置是否对监听自身消息</span><br><span class="line">set qq default<span class="built_in">_</span>bot 主机器人账号 <span class="params">#</span>傻妞支持对接多个qq，设置主qq机器人</span><br><span class="line">set qq masters q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置qq管理员</span><br><span class="line">set qq notifier q1<span class="built_in">&amp;</span>q2<span class="built_in">&amp;</span>q3... 设置接受通知的qq账号</span><br><span class="line"><span class="params">#</span> 对接telegtam</span><br><span class="line">set tg token ? <span class="params">#</span>设置telegram机器人token</span><br><span class="line">set tg http<span class="built_in">_</span>proxy ? <span class="params">#</span>设置telegram机器人代理</span><br><span class="line">set tg masters t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... <span class="params">#</span>设置telegram机器人管理员</span><br><span class="line">set tg notifier t1<span class="built_in">&amp;</span>t2<span class="built_in">&amp;</span>t3... 设置接受通知的telegram账号</span><br><span class="line"><span class="params">#</span> 对接微信公众号</span><br><span class="line">set wxmp app<span class="built_in">_</span>id ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>id</span><br><span class="line">set wxmp app<span class="built_in">_</span>secret ? <span class="params">#</span>设置微信公众平台app<span class="built_in">_</span>secret </span><br><span class="line">set wxmp token ? <span class="params">#</span>设置微信公众平台token</span><br><span class="line">set wxmp encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key ? <span class="params">#</span>设置微信公众平台encoding<span class="built_in">_</span>aes<span class="built_in">_</span>key</span><br><span class="line">set wxmp masters w1<span class="built_in">&amp;</span>w2<span class="built_in">&amp;</span>w3 <span class="params">#</span>设置微信公众平台管理员</span><br><span class="line">set wxmp subscribe<span class="built_in">_</span>reply ? <span class="params">#</span>设置公众号关注事件回复</span><br><span class="line">set wxmp default<span class="built_in">_</span>reply 无法回复该消息 设置公众号默认回复</span><br><span class="line"><span class="params">#</span> 对接微信-可爱猫</span><br><span class="line">set wx api<span class="built_in">_</span>url ? <span class="params">#</span>设置插件调用地址，确保傻妞可以访问可爱猫端口</span><br><span class="line">set wx keaimao<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置可爱猫是否动态网络地址，适用于可爱猫家庭宽带而傻妞在云服务器的情况下</span><br><span class="line">set wx keaimao<span class="built_in">_</span>port ? <span class="params">#</span>设置可爱猫端口</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，不知道不用填</span></span><br><span class="line">set wx sillyGirl<span class="built_in">_</span>dynamic<span class="built_in">_</span>ip true <span class="params">#</span>设置傻妞是否动态网络地址，适用于傻妞家庭宽带而可爱猫在云服务器的情况下</span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br><span class="line"><span class="params">#</span> 对接微信-vlw</span><br><span class="line">set wx vlw<span class="built_in">_</span>addr http://vlw插件ip:端口 <span class="params">#</span>设置插件调用地址，对应之前插件配置的 序号3 HTTP 外网API调用地址</span><br><span class="line">set wx vlw<span class="built_in">_</span>token XXX <span class="params">#</span>设置对接vlw插件的token，对应之前插件配置的 序号2 API调用Token，例如sillyGirl</span><br><span class="line">set wx relay<span class="built_in">_</span>mode true <span class="params">#</span>设置图片转发模式，否则可能会出现此图片来自xx未经允许不得使用的提示。不懂就不要设置了。</span><br><span class="line">set wx relaier ? <span class="params">#</span>设置指定转发地址，格式为 https://域名/relay?url=<span class="comment">%s，这个我也不知道干嘛的，不知道就别设置了。</span></span><br><span class="line">set sillyGirl enable<span class="built_in">_</span>http<span class="built_in">_</span>server true <span class="params">#</span>启动http服务，一定要打开</span><br></pre></td></tr></table></figure><h4 id="对接青龙"><a href="#对接青龙" class="headerlink" title="对接青龙"></a>对接青龙</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 对接青龙</span><br><span class="line">青龙管理 <span class="params">#</span>多容器青龙配置</span><br><span class="line">ql spy <span class="params">#</span>自定义监听变量运行青龙指定脚本功能</span><br><span class="line"><span class="params">#</span> 使用命令</span><br><span class="line">ql config <span class="params">#</span>获取青龙config.sh设置的内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql envs <span class="params">#</span>获取青龙config.sh内设置的所有环境变量内容，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env get ? <span class="params">#</span>获取青龙config.sh内指定环境变量的内容，仅精确匹配</span><br><span class="line">ql env find ? <span class="params">#</span>查找青龙config.sh内指定的环境变量内容，支持模糊匹配</span><br><span class="line">ql env set ? ? <span class="params">#</span>在青龙config.sh内设置环境变量</span><br><span class="line">ql env remark ? ? <span class="params">#</span>字面意思是给环境变量设置备注，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql env disable ? <span class="params">#</span>禁用（注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql env enable ? <span class="params">#</span>启用（取消注释）在青龙config.sh内设置的环境变量</span><br><span class="line">ql raw ? <span class="params">#</span>下载raw链接的js</span><br><span class="line">ql task ? <span class="params">#</span>在青龙里面运行指定的js</span><br><span class="line">ql repo ? <span class="params">#</span>在青龙里面拉库，例：ql repo https://github.com/cdle/carry.git</span><br><span class="line">ql cookie status <span class="params">#</span>查询青龙里cookie状态</span><br><span class="line">ql crons <span class="params">#</span>获取青龙所有定时任务，但我没测试出来结果，等猫咪大佬解惑</span><br><span class="line">ql cron status ? <span class="params">#</span>查看青龙指定定时任务的状态</span><br><span class="line">ql cron run ? <span class="params">#</span>运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron stop ? <span class="params">#</span>停止运行青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron enable ? <span class="params">#</span>启用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron disable ? <span class="params">#</span>禁用青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron find ? <span class="params">#</span>查找青龙定时任务，支持模糊匹配</span><br><span class="line">ql cron logs ? <span class="params">#</span>查找青龙定时任务运行日志，支持模糊匹配</span><br><span class="line">ql cron hide duplicate <span class="params">#</span>隐藏青龙重复定时任务</span><br><span class="line">set qinglong autoCronHideDuplicate false <span class="params">#</span>关闭自动隐藏任务命令</span><br></pre></td></tr></table></figure><h4 id="对接芝士"><a href="#对接芝士" class="headerlink" title="对接芝士"></a>对接芝士</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 对接芝士</span><br><span class="line">h <span class="params">#</span>短信登录</span><br><span class="line">q <span class="params">#</span>退出短信登录</span><br><span class="line">登录 <span class="params">#</span>短信登录</span><br><span class="line">登陆 <span class="params">#</span>短信登录</span><br><span class="line">查询 ? <span class="params">#</span>查询指定账号的资产</span><br><span class="line">资产推送 <span class="params">#</span>向所有绑定了账号的用户推送资产</span><br><span class="line">查询 <span class="params">#</span>查询当前社交账号绑定的所有账号资产</span><br><span class="line">关闭?通知 <span class="params">#</span>关闭指定活动任务通知</span><br><span class="line">任务通知 <span class="params">#</span>推送账号失效、果园和萌宠成熟以及未继续种植通知。有私聊和群聊@两种方式，默认私聊。</span><br><span class="line">账号管理 <span class="params">#</span>自定义任务通知等</span><br><span class="line">推送管理 <span class="params">#</span>用户设置推送</span><br><span class="line">jd asset ? <span class="params">#</span>查询指定账号的资产</span><br><span class="line">jd imOf ? <span class="params">#</span>获取绑定的社交账号，仅精确匹配pt<span class="built_in">_</span>pin值</span><br><span class="line">jd find ? <span class="params">#</span>查找对应账号的编号、pt<span class="built_in">_</span>pin值、备注等信息，支持编号、pt<span class="built_in">_</span>pin值、备注查询，支持连号查询</span><br><span class="line">jd exchange ? ? <span class="params">#</span>交换两个账号的序号位置</span><br><span class="line">jd enable ? <span class="params">#</span>启用指定账号</span><br><span class="line">jd disable ? <span class="params">#</span>禁用指定账号</span><br><span class="line">jd remark ? ? <span class="params">#</span>备注指定账号</span><br><span class="line">jd remove ? <span class="params">#</span>跨容器删除ck，?可以匹配整个ck和相应的备注。芝士只有这一种方式删除ck</span><br><span class="line">jd send ? ? <span class="params">#</span>给指定账号发送消息</span><br><span class="line">jd unbind <span class="params">#</span>解绑该社交账号下的某栋账号</span><br><span class="line">pt<span class="built_in">_</span>key=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+); pt<span class="built_in">_</span>pin=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+) <span class="params">#</span>发送ck提交到青龙</span><br><span class="line">pin=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+); wskey=([<span class="built_in">^</span>;=<span class="keyword">\s</span>]+) <span class="params">#</span>发送wskey提交到青龙</span><br><span class="line">packetId=?(<span class="built_in">&amp;</span>|<span class="built_in">&amp;</span>amp;)currentActId <span class="params">#</span>极速推一推助力</span><br><span class="line">set jd<span class="built_in">_</span>cookie notify<span class="built_in">_</span>mode group <span class="params">#</span>任务通知设置群聊模式</span><br><span class="line">set jd<span class="built_in">_</span>cookie qqGroup ? <span class="params">#</span>任务通知设置qq群聊ID</span><br><span class="line">set jd<span class="built_in">_</span>cookie wxGroup ? <span class="params">#</span>任务通知设置微信群聊ID</span><br><span class="line">set pinQQ pt<span class="built_in">_</span>pin qq号码 <span class="params">#</span>ck账号绑定qq号码，例 set pinQQ jd<span class="built_in">_</span>xxxxxx 123456</span><br><span class="line">delete pinQQ pt<span class="built_in">_</span>pin，<span class="params">#</span>取消ck账号绑定qq号码，例 delete pinQQ jd<span class="built_in">_</span>xxxxxx</span><br><span class="line">set pinTG pt<span class="built_in">_</span>pin TGID <span class="params">#</span>ck账号绑定TG账号，TGID找getmyid获取</span><br><span class="line">delete pinTG pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定TG账号</span><br><span class="line">set pinWX pt<span class="built_in">_</span>pin 微信号 <span class="params">#</span>微信给傻妞bot发送myuid获取，其实就是你的微信号，不是微信昵称。</span><br><span class="line">delete pinWX pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定微信账号</span><br><span class="line">set pinWXMP pt<span class="built_in">_</span>pin <span class="params">#</span>微信公众号用户id <span class="params">#</span>给公众号发送myuid获取</span><br><span class="line">delete pinWXMP pt<span class="built_in">_</span>pin <span class="params">#</span>取消ck账号绑定微信公众号用户号</span><br><span class="line">set jdWSCK update 56 * * * * <span class="params">#</span>设置wskey自动转cooke定时：</span><br><span class="line">set jd<span class="built_in">_</span>cookie query<span class="built_in">_</span>wait<span class="built_in">_</span>time [限制秒数] <span class="params">#</span>限制查询频率 例:set jd<span class="built_in">_</span>cookie query<span class="built_in">_</span>wait<span class="built_in">_</span>time 60</span><br><span class="line">set jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr 阿东ip:端口 <span class="params">#</span>设置阿东登录地址，不需要http，仅支持阿东1.7及以下版本</span><br><span class="line">set jd<span class="built_in">_</span>cookie selfQid 机器人qq账号 <span class="params">#</span>设置阿东qq机器人账号，仅支持阿东1.7及以下版本</span><br><span class="line">set jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr http://诺兰ip:端口 <span class="params">#</span>设置诺兰登陆地址，需要http</span><br><span class="line">set jd<span class="built_in">_</span>cookie login<span class="built_in">_</span>num ? <span class="params">#</span>设置登录坑位</span><br><span class="line">delete jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr 例：delete jd<span class="built_in">_</span>cookie adong<span class="built_in">_</span>addr ip:5701 <span class="params">#</span>删除阿东地址</span><br><span class="line">delete jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr 例：delete jd<span class="built_in">_</span>cookie nolan<span class="built_in">_</span>addr http://ip:5701 <span class="params">#</span>删除nolan地址</span><br><span class="line">set jd<span class="built_in">_</span>cookie xdd<span class="built_in">_</span>url ?  <span class="params">#</span>短信登录接入xdd指令，格式http://IP地址:端口/api/login/smslogin</span><br><span class="line">set jd<span class="built_in">_</span>cookie xdd<span class="built_in">_</span>token ? <span class="params">#</span>对接xdd，额外设置参数</span><br><span class="line">set jd<span class="built_in">_</span>cookie asset<span class="built_in">_</span>push ? <span class="params">#</span>设置定时推送</span><br><span class="line">set jd<span class="built_in">_</span>cookie ad ? <span class="params">#</span>自定义广告，成功登录后发送</span><br><span class="line">set jd<span class="built_in">_</span>cookie asset<span class="built_in">_</span>query<span class="built_in">_</span>alias xxxxxxx <span class="params">#</span>自定义查询口令，变相实现屏蔽查询口令。</span><br><span class="line">set jd<span class="built_in">_</span>cookie disable<span class="built_in">_</span>notify true <span class="params">#</span>关闭推送指令，不想收到请jd unbind</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>auto<span class="built_in">_</span>update true <span class="params">#</span>自动检测ck有效性开关，关闭则设置为false。</span><br><span class="line">jd check ? ?</span><br><span class="line">jd myCookie <span class="params">#</span>查询绑定ck</span><br><span class="line">set jd<span class="built_in">_</span>cookie enable<span class="built_in">_</span>yad false <span class="params">#</span>跳过云上阿东。</span><br><span class="line">set jd<span class="built_in">_</span>cookie login<span class="built_in">_</span>tip xxx <span class="params">#</span>你也可以自定义登录提示</span><br><span class="line">set jd<span class="built_in">_</span>cookie sms<span class="built_in">_</span>tip xxx <span class="params">#</span>接收短信验证码提示</span><br><span class="line">set silly http<span class="built_in">_</span>addr http://192.168.31.233:8080 <span class="params">#</span>失效ck无法禁用的，检查在青龙自动生成的傻妞地址GOTIFY<span class="built_in">_</span>URL，特定网络环境手动设置</span><br></pre></td></tr></table></figure><h4 id="对接小爱同学"><a href="#对接小爱同学" class="headerlink" title="对接小爱同学"></a>对接小爱同学</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 小爱同学，可以指定API，对小爱说对话模式即可开启连续对话模式，闭嘴可关闭。</span><br><span class="line">set sillyGirl 小爱同学 http://81.70.100.130/api/xiaoai.php?msg=<span class="comment">%s&amp;n=text #挂了就自己换</span></span><br><span class="line">set sillyGirl 小爱同学 http://jiuli.xiaoapi.cn/i/xiaoai<span class="built_in">_</span>tts.php?msg=<span class="comment">%s #挂了就自己换</span></span><br><span class="line"><span class="params">#</span> 处理json格式数据的小爱api，不填获取整个文本</span><br><span class="line">set sillyGirl 小爱同学gjson text</span><br><span class="line"><span class="params">#</span> 删除处理json格式数据的小爱api</span><br><span class="line">silly delete 小爱同学gjson</span><br><span class="line"><span class="params">#</span> 使用方法：</span><br><span class="line">1、唤起小爱：发送“小爱+想说的话”，例如：小爱今天天气。</span><br><span class="line">2、对话模式：发送“小爱对话模式”开启，发送“闭嘴”结束。</span><br><span class="line">3、成语接龙：发送“成语接龙”开始。这个可应该不是小爱同学的功能，但我懒得单独写它了。</span><br></pre></td></tr></table></figure><h4 id="对接返利"><a href="#对接返利" class="headerlink" title="对接返利"></a>对接返利</h4><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line"><span class="params">#</span> 授权正版流程 前提请保证qqmasters第一个qq和群qq号一致</span><br><span class="line">群里发送命令fanli<span class="built_in">_</span>vip，按机器人指引继续往下走</span><br><span class="line">给你的qq/tg/微信机器人发送命令machineId，会得到一串代码，复制下来</span><br><span class="line">将刚刚的一串代码发送到付费群，会要求你输入密钥，随便自定义一个密钥，假如是“123”</span><br><span class="line">给你的qq/tg/微信机器人依次发送下面的命令</span><br><span class="line">set otto fanli<span class="built_in">_</span>vip<span class="built_in">_</span>secret 123 <span class="params">#1</span>23就是你刚刚自己设置的密钥</span><br><span class="line">fanli<span class="built_in">_</span>vip <span class="params">#</span>如正常会返回“傻妞返利插件正版授权用户。”</span><br><span class="line">time <span class="params">#</span>如返回盗版，尝试发送time查看系统时间</span><br><span class="line"><span class="params">#</span>配置返利 傻瓜式交互配置，根据机器人提示操作即可。</span><br><span class="line">fanli<span class="built_in">_</span>edit</span><br><span class="line"><span class="params">#</span>采集TG频道线报，设置代理，支持sock5和http代理。fanli<span class="built_in">_</span>edit选择频道，填写格式为频道ID，如ifiscat，多个用空格隔开。</span><br><span class="line">set tg http<span class="built_in">_</span>prxy 代理地址</span><br><span class="line"><span class="params">#</span>增加排除关键词excepts、包含关键词extracts</span><br><span class="line"><span class="params">##</span>全局控制</span><br><span class="line">extracts</span><br><span class="line">excepts</span><br><span class="line"><span class="params">##</span>平台控制</span><br><span class="line">jd<span class="built_in">_</span>extracts</span><br><span class="line">jd<span class="built_in">_</span>excepts</span><br><span class="line">tb<span class="built_in">_</span>extracts</span><br><span class="line">tb<span class="built_in">_</span>excepts</span><br><span class="line">以上变量通过命令编辑，设置全局排除关键词例子：set otto excepts [&quot;加群&quot;, &quot;助力&quot;]，这样可以屏蔽包含加群和助力的字样的线报。</span><br><span class="line"><span class="params">##</span>群组控制</span><br><span class="line">举例：设置转发群&#123;&quot;imType&quot;: &quot;wx&quot;, &quot;groupCode: 10000&quot;, &quot;excepts&quot;: [&quot;绿&quot;], &quot;extracts&quot;: [&quot;帽子&quot;]&#125;，这样就实现了一个帽子线报群，而且蔽绿了帽子。</span><br><span class="line"><span class="params">#</span> 设置京品库appid</span><br><span class="line">set otto jingpinku<span class="built_in">_</span>appid ?</span><br><span class="line"><span class="params">#</span> 设置京品库appkey</span><br><span class="line">set otto jingpinku<span class="built_in">_</span>appkey ?</span><br><span class="line"><span class="params">#</span> 设置京东联盟id</span><br><span class="line">set otto jd<span class="built_in">_</span>union<span class="built_in">_</span>id ? <span class="params">#</span>联盟id京粉app中查看。</span><br><span class="line"><span class="params">#</span>关闭回复自身</span><br><span class="line">set qq onself false</span><br><span class="line"><span class="params">#</span> 采集京东线报群，需先让机器人加入群</span><br><span class="line">set otto jd<span class="built_in">_</span>spy<span class="built_in">_</span>on [&#123;imType:&quot;qq&quot;,groupCode: 采集的群&#125;, &#123;imType:&quot;qq&quot;,groupCode: 采集的群&#125;]</span><br><span class="line"><span class="params">#</span> 转发转链群</span><br><span class="line">set otto jd<span class="built_in">_</span>spy<span class="built_in">_</span>home [&#123;imType:&quot;qq&quot;,groupCode: 转发qq群号&#125;, &#123;imType:&quot;wx&quot;,groupCode: 转发wx群号&#125;]</span><br><span class="line"><span class="params">#</span> 折淘客对接秘钥appkey</span><br><span class="line">set otto zhetaoke<span class="built_in">_</span>appkey ?</span><br><span class="line"><span class="params">#</span> 淘客账号授权ID</span><br><span class="line">set otto taobao<span class="built_in">_</span>sid ?</span><br><span class="line"><span class="params">#</span> 淘客PID      </span><br><span class="line">set otto taobao<span class="built_in">_</span>pid ?</span><br><span class="line"><span class="params">#</span> 关键词屏蔽，用<span class="built_in">&amp;</span>连接</span><br><span class="line">set otto tb<span class="built_in">_</span>avoids 关键词1<span class="built_in">&amp;</span>关键词2...</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>Docker拉取指定架构镜像</title>
      <link>https://log1992.cn/2023/06/02/play/Docker%E6%8B%89%E5%8F%96%E6%8C%87%E5%AE%9A%E6%9E%B6%E6%9E%84%E9%95%9C%E5%83%8F/</link>
      <description>
        <![CDATA[<blockquote>
<p>服务器没有魔法，无法正常拉取<a href="https://hub.docker.com/">DockerHub</a>的镜像，有些镜像源又没有该镜像，只能手动在电脑上拉下来重新上传了。</p>
</blockquote>
<h4 id="拉取"><a href="#拉取" class="headerlink" title="拉取"></a>拉取</h4><p>我]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/play/">折腾</category>
      <category domain="https://log1992.cn/tags/Docker/">Docker</category>
      <pubDate>Fri, 02 Jun 2023 03:31:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>服务器没有魔法，无法正常拉取<a href="https://hub.docker.com/">DockerHub</a>的镜像，有些镜像源又没有该镜像，只能手动在电脑上拉下来重新上传了。</p></blockquote><h4 id="拉取"><a href="#拉取" class="headerlink" title="拉取"></a>拉取</h4><p>我们一般使用</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker pull xhofe/alist:latest</span><br></pre></td></tr></table></figure><p>拉取镜像，但是由于我自己是M芯片的Mac，导致拉下来的镜像是arm64的，其他设备使用时就会出现架构错误，那么如何解决呢？</p><h4 id="针对架构拉取"><a href="#针对架构拉取" class="headerlink" title="针对架构拉取"></a>针对架构拉取</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker pull --platform linux/amd64 xhofe/alist:latest</span><br></pre></td></tr></table></figure><p>在拉取时带上<code>platform</code>，后面加上OS&#x2F;ARCH</p><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">linux/amd64</span><br><span class="line">linux/386</span><br><span class="line">linux/arm/v6</span><br><span class="line">linux/arm/v7</span><br><span class="line">linux/s390x</span><br></pre></td></tr></table></figure><p>这样就能拉取到我们想要架构的镜像了。</p><h4 id="导出"><a href="#导出" class="headerlink" title="导出"></a>导出</h4><p>使用<code>docker save</code>命令导出</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker save xhofe/alist &gt; /Users/logan/Downloads/alist.tar</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>Android中NFC的使用</title>
      <link>https://log1992.cn/2023/06/01/android/Android%E4%B8%ADNFC%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
      <description>
        <![CDATA[<blockquote>
<p>最近需要将先前项目的芯片识别功能优化，先前的项目使用的是RFID高频芯片识别+二维码扫描处理，后续设备大概率不会上RFID设备，因此需要接入NFC识别。</p>
</blockquote>
<h4 id="什么是NFC"><a href="#什么是NFC" class="headerlink" title="什么是NFC"></a>什么是NFC</h4><p>NFC]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/NFC/">NFC</category>
      <pubDate>Thu, 01 Jun 2023 06:30:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>最近需要将先前项目的芯片识别功能优化，先前的项目使用的是RFID高频芯片识别+二维码扫描处理，后续设备大概率不会上RFID设备，因此需要接入NFC识别。</p></blockquote><h4 id="什么是NFC"><a href="#什么是NFC" class="headerlink" title="什么是NFC"></a>什么是NFC</h4><p>NFC是近场通信(Near Field Communication,NFC)，是一种短距高频的无线电技术。</p><p>由非接触式射频识别(RFID)演变而来。NFC工作频率为13.56Hz，有效范围为20cm以内，其传输速度有106 Kbit&#x2F;秒、212 Kbit&#x2F;秒或者424 Kbit&#x2F;秒三种。NFC采用主动和被动两种读取模式。</p><h4 id="如何接入"><a href="#如何接入" class="headerlink" title="如何接入"></a>如何接入</h4><p>Android系统自带NFC，在手机存在NFC硬件时就可以使用了。早在Android2.3时，我们就能使用Android Beam在同步的设备间通过NFC传递图片等数据。通过查阅源码我们得知，NFC属于系统服务，在使用时自然是需要通过<code>context.getSystemService(Context.NFC_SERVICE);</code>来获取系统服务</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Use with &#123;<span class="doctag">@link</span> #getSystemService(String)&#125; to retrieve a</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> android.nfc.NfcManager&#125; for using NFC.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #getSystemService(String)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NFC_SERVICE</span> <span class="operator">=</span> <span class="string">&quot;nfc&quot;</span>;</span><br></pre></td></tr></table></figure><p>我们需要构建一个<code>NfcManager</code>来使用NFC，而<code>NfcManager</code>中只有一个主角，那就是<code>NfcAdapter</code>!</p><p>想要获得<code>NfcAdapter</code>，只需要使用静态方法</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">NfcAdapter.getDefaultAdapter(<span class="built_in">this</span>);</span><br></pre></td></tr></table></figure><p>其中的<code>context</code>用来启动NFC服务，等价于：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">NfcManager</span> <span class="variable">manager</span> <span class="operator">=</span>(NfcManager)context.getSystemService(Context.NFC_SERVICE);</span><br><span class="line"><span class="type">NfcAdapter</span> <span class="variable">adapter</span> <span class="operator">=</span> manager.getDefaultAdapter();</span><br></pre></td></tr></table></figure><p>通过<code>NfcAdapter</code>的<code>enableForegroundDispatch()</code>方法启用对给定Activity的前台分发，即最高优先级的分发（优先级这个后面再说）。</p><figure class="highlight tex"><table><tr><td class="code"><pre><span class="line">This method must be called from the main thread, and only when the activity is in the foreground (resumed). Also, activities must call disableForegroundDispatch before the completion of their Activity.onPause callback to disable foreground dispatch after it has been enabled.</span><br></pre></td></tr></table></figure><p>方法的注释也写的很清楚，必须在主线程调用，同时必须当前<code>activity</code>可见&#x2F;在前台，同样的，我们需要在<code>activity</code>不可见的时候调用<code>disableForegroundDispatch()</code>方法。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onResume</span><span class="params">()</span> &#123;</span><br><span class="line">       <span class="built_in">super</span>.onResume();</span><br><span class="line">       <span class="comment">//设置处理优于所有其他NFC的处理</span></span><br><span class="line">       <span class="keyword">if</span> (mNfcAdapter != <span class="literal">null</span>)</span><br><span class="line">           mNfcAdapter.enableForegroundDispatch(<span class="built_in">this</span>, mPendingIntent, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onPause</span><span class="params">()</span> &#123;</span><br><span class="line">       <span class="built_in">super</span>.onPause();</span><br><span class="line">       <span class="comment">//恢复默认状态</span></span><br><span class="line">       <span class="keyword">if</span> (mNfcAdapter != <span class="literal">null</span>)</span><br><span class="line">           mNfcAdapter.disableForegroundDispatch(<span class="built_in">this</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>那么基本的就是这个成对出现的方法了。</p><p><code>enableForegroundDispatch()</code>方法中需要一个<code>PendingIntent</code>，下面是我们构建的<code>PendingIntent</code></p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//一旦截获NFC消息，就会通过PendingIntent调用窗口</span></span><br><span class="line">        mPendingIntent = PendingIntent.getActivity(<span class="built_in">this</span>, <span class="number">0</span>, <span class="keyword">new</span> <span class="title class_">Intent</span>(<span class="built_in">this</span>, getClass()), <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>构建<code>PendingIntent</code>时我们传入的<code>Intent</code>直接使用了<code>getClass()</code>，因此，回调的数据会到当前的<code>activity</code>中，我们需要重载<code>onNewIntent()</code>方法来处理我们接收到的数据。</p><h4 id="如何读取"><a href="#如何读取" class="headerlink" title="如何读取"></a>如何读取</h4><p>上面说道在<code>onNewIntent()</code>方法中就能处理我们收到的NFC数据了，这就不得不说另外一个重要的对象<code>Tag</code>了</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//1.获取Tag对象</span></span><br><span class="line">        <span class="type">Tag</span> <span class="variable">detectedTag</span> <span class="operator">=</span> intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);</span><br></pre></td></tr></table></figure><p>通过<code>Intent</code>获取<code>Tag</code>对象</p><p><img src="https://image.log1992.cn/uPic/image-20230601155044920.png"></p><p>通过注释可知：</p><p>我们可以通过<code>Tag</code>的<code>getId()</code>方法获取ID，通过<code>getTechList()</code>方法获取其技术集，技术集代表了当前这个芯片支持的技术，通俗点就是可以支持的格式，或者说是实现某些高级功能的基础支撑。</p><p>每次发现标记(进入范围)时都会创建一个新的标记对象，当发现一个标记时，将创建一个tag对象，并通过NfcAdapter传递给<code>activity</code>。没错，就是<code>PendingIntent</code>申明的那个。</p><p>接下来<code>Tag</code>会经过四个阶段的分发:</p><ol><li><p>前台活动<code>activity</code>分发</p><p>调用了<code>NfcAdapter.enableForegroundDispatch()</code>方法的<code>activity</code>，这个会被优先触发，比如我们平时打开系统NFC，贴上NFC卡片后，系统会提示“检测到NFC卡片”，然后会给我们一些推荐的操作，诸如打开微信，支付宝充值公交卡等等。但是当我们在调用了<code>NfcAdapter.enableForegroundDispatch()</code>方法的<code>activity</code>可见时，系统的处理就会因为优先级不够而不被触发，转而在当前<code>activity</code>回调数据</p></li><li><p>NDEF数据分发<br> 如果标签中包含NDEF数据，系统会在第一个NdefMessage中检查第一个NdefRecord，</p></li></ol><p>  NdefRecord可以有很多种类型，其构造方法如下</p>  <figure class="highlight java"><table><tr><td class="code"><pre><span class="line">NdefRecord(<span class="type">short</span> tnf, <span class="type">byte</span>[] type, <span class="type">byte</span>[] id, <span class="type">byte</span>[] payload)</span><br></pre></td></tr></table></figure><p>  具体参数说明可以查看源码。</p><p>  我们可以在这个地方构建Android Application Record，</p><p>  通过<code>createApplicationRecord()</code>方法，例如：</p>  <figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">NdefMessage</span> <span class="variable">ndefMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NdefMessage</span>(<span class="keyword">new</span> <span class="title class_">NdefRecord</span>[]&#123;NdefRecord</span><br><span class="line">                .createApplicationRecord(<span class="string">&quot;com.android.mms&quot;</span>)&#125;);</span><br></pre></td></tr></table></figure><p>  我们将这样的NdefRecord写入标签后，下次我们识别标签就会自动打开短信应用。</p>  <figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">NdefMessage</span> <span class="variable">ndefMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NdefMessage</span>(<span class="keyword">new</span> <span class="title class_">NdefRecord</span>[]&#123;createUriRecord(<span class="string">&quot;http://www.baidu.com&quot;</span>)&#125;);</span><br></pre></td></tr></table></figure><p>  或者</p>  <figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">NdefMessage</span> <span class="variable">ndefMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NdefMessage</span>(<span class="keyword">new</span> <span class="title class_">NdefRecord</span>[]&#123;NdefRecord.createUri(Uri.parse(<span class="string">&quot;http://www.baidu.com&quot;</span>))&#125;);</span><br></pre></td></tr></table></figure><p>  构建Uri的Record，我们可以打开网页。</p><p>  当然，我们也可以直接写入文本数据</p>  <figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">NdefMessage</span> <span class="variable">ndefMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NdefMessage</span>(<span class="keyword">new</span> <span class="title class_">NdefRecord</span>[]&#123;createTextRecord(<span class="string">&quot;Hello World&quot;</span>)&#125;);</span><br></pre></td></tr></table></figure><ol start="3"><li><p>标签技术分发</p><p>当<code>activity</code>注册时，添加了相应的<code>intent-filter</code>，例如：</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">&quot;.nfc.TechFilter&quot;</span> <span class="attr">android:label</span>=<span class="string">&quot;NFC/TechFilter&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- Add a technology filter --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">&quot;android.nfc.action.TECH_DISCOVERED&quot;</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></span><br><span class="line"> </span><br><span class="line">      <span class="tag">&lt;<span class="name">meta-data</span> <span class="attr">android:name</span>=<span class="string">&quot;android.nfc.action.TECH_DISCOVERED&quot;</span></span></span><br><span class="line"><span class="tag">          <span class="attr">android:resource</span>=<span class="string">&quot;@xml/filter_nfc&quot;</span></span></span><br><span class="line"><span class="tag">      /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">activity</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resources</span> <span class="attr">xmlns:xliff</span>=<span class="string">&quot;urn:oasis:names:tc:xliff:document:1.2&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- capture anything using NfcF --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">tech-list</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">tech</span>&gt;</span>android.nfc.tech.NfcF<span class="tag">&lt;/<span class="name">tech</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">tech-list</span>&gt;</span></span><br><span class="line"> </span><br><span class="line">      <span class="comment">&lt;!-- OR --&gt;</span></span><br><span class="line"> </span><br><span class="line">      <span class="comment">&lt;!-- capture all MIFARE Classics with NDEF payloads --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">tech-list</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">tech</span>&gt;</span>android.nfc.tech.NfcA<span class="tag">&lt;/<span class="name">tech</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">tech</span>&gt;</span>android.nfc.tech.MifareClassic<span class="tag">&lt;/<span class="name">tech</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">tech</span>&gt;</span>android.nfc.tech.Ndef<span class="tag">&lt;/<span class="name">tech</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">tech-list</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">resources</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在<code>intent-filter</code>中添加对应的<code>action</code>后，我们就能通过<code>action</code>获取数据了</p></li><li><p>备用分发</p><p>如果所有的分发条件都不满足，就会走<code>NfcAdapter.ACTION_TAG_DISCOVERED</code>的<code>action</code>来分发数据</p></li></ol><p>读取数据我们可以根据当前的<code>techType</code>来读取，例如：</p><p><img src="https://image.log1992.cn/uPic/image-20230601171030647.png"></p><p>当前芯片的<code>TechList</code>中有3条数据，分别是NFCA，MifareUltralight和NEDF，因此我们就能针对性的取数据了。</p><p>NEDF通过<code>Ndef ndef = Ndef.get(detectedTag);</code>来获取NEDF实例；</p><p>MifareUltralight通过<code>MifareUltralight ultralight = MifareUltralight.get(tag);</code>来获取MifareUltralight实例；</p><p>…</p><p>以此类推</p><p>拿到相应的实例对象后，先判断是否为空，因为数据写入的格式会决定数据写入成哪种<code>TechType</code>的数据，后面取值就简单了，拿到对应的<code>NdefRecord</code>解析就行了.</p><p>读取NDEF：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> String <span class="title function_">readNdefTag</span><span class="params">(Intent intent)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) &#123;</span><br><span class="line">            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(</span><br><span class="line">                    NfcAdapter.EXTRA_NDEF_MESSAGES);</span><br><span class="line">            NdefMessage msgs[] = <span class="literal">null</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">contentSize</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span> (rawMsgs != <span class="literal">null</span>) &#123;</span><br><span class="line">                msgs = <span class="keyword">new</span> <span class="title class_">NdefMessage</span>[rawMsgs.length];</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; rawMsgs.length; i++) &#123;</span><br><span class="line">                    msgs[i] = (NdefMessage) rawMsgs[i];</span><br><span class="line">                    contentSize += msgs[i].toByteArray().length;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (msgs != <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="type">NdefRecord</span> <span class="variable">record</span> <span class="operator">=</span> msgs[<span class="number">0</span>].getRecords()[<span class="number">0</span>];</span><br><span class="line">                    <span class="type">String</span> <span class="variable">textRecord</span> <span class="operator">=</span> NFCUtils.parseTextRecord(record);</span><br><span class="line">                    <span class="keyword">return</span> textRecord;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>读取MifareUltralight：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">readTag</span><span class="params">(Tag tag)</span> &#123;</span><br><span class="line">        <span class="type">MifareUltralight</span> <span class="variable">ultralight</span> <span class="operator">=</span> MifareUltralight.get(tag);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            ultralight.connect();</span><br><span class="line">            <span class="comment">//从第4索引开始读，前4索引对应的为标签固化区</span></span><br><span class="line">            <span class="type">byte</span>[] data = ultralight.readPages(<span class="number">4</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>(data, Charset.forName(<span class="string">&quot;GB2312&quot;</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ultralight.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>MifareUltralight读取涉及到NFC标签的分区，前4个分区(索引0-3)为固有分区，后面的为数据区，我们要读，要写都要写到数据区中，并且在写的时候需要注意长度。</p><h4 id="如何写入"><a href="#如何写入" class="headerlink" title="如何写入"></a>如何写入</h4><p>下面是MifareUltralight写入的方法</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writeTag</span><span class="params">(Tag tag,String data)</span> &#123;</span><br><span class="line">        <span class="comment">//向nfc标签写数据第1步,从标签中得到MifareUltralight</span></span><br><span class="line">        <span class="type">MifareUltralight</span> <span class="variable">ultralight</span> <span class="operator">=</span> MifareUltralight.get(tag);</span><br><span class="line">        <span class="keyword">if</span> (TextUtils.isEmpty(data)) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//向nfc标签写数据第2步, connect</span></span><br><span class="line">            ultralight.connect();</span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">            * 向nfc标签写数据第3步, 正式写数据.前4页（0至3）存储了NFC标签相关的信息</span></span><br><span class="line"><span class="comment">            * 注意 Charset.forName(&quot;GB2312&quot;)),</span></span><br><span class="line"><span class="comment">            * 不用utf-8因为一个汉字有可能用3个字节编码汉字,那么2个汉字有可能是6个字节.</span></span><br><span class="line"><span class="comment">            * 而GB2312始终用2个字节.而每页最多4个字节,</span></span><br><span class="line"><span class="comment">            */</span></span><br><span class="line">            ultralight.writePage(<span class="number">4</span>, data.substring(<span class="number">0</span>,<span class="number">4</span>).getBytes(Charset.forName(<span class="string">&quot;GB2312&quot;</span>)));<span class="comment">//第4页,页从0开始.</span></span><br><span class="line">            ultralight.writePage(<span class="number">5</span>, data.substring(<span class="number">4</span>,<span class="number">8</span>).getBytes(Charset.forName(<span class="string">&quot;GB2312&quot;</span>)));</span><br><span class="line"></span><br><span class="line">            ToastUtils.showShort( <span class="string">&quot;成功写入MifareUltralight格式数据&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ultralight.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>汉字的编码长度在不同的编码下会有不同的占位。</p><p>M1卡有从0到15共16个扇区，每个扇区配备了从0到3共4个段，每个段可以保存16字节的内容，我们一般可以存1024字节的数据，根据我们写入的数据长度，要确认下面的数据要写到哪个扇区里，还是比较麻烦的，另外，MifareUltralight读取时会把后面扇区的空数据也读出来，生成乱码，所以不太推荐使用MifareUltralight读取。</p><p>写入NDEF数据就比较方便了</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//首先构建NdefMessage对象</span></span><br><span class="line"><span class="type">NdefMessage</span> <span class="variable">ndefMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NdefMessage</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">NdefRecord</span>[]&#123;createTextRecord(mText)&#125;);</span><br><span class="line">                </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">writeTag</span><span class="params">(NdefMessage ndefMessage, Tag tag)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Ndef</span> <span class="variable">ndef</span> <span class="operator">=</span> Ndef.get(tag);</span><br><span class="line">            ndef.connect();</span><br><span class="line">            ndef.writeNdefMessage(ndefMessage);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>直接拿到<code>Ndef</code>对象调用<code>writeNdefMessage()</code>就可以了。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>另辟蹊径屏蔽截图限制</title>
      <link>https://log1992.cn/2023/05/31/android/%E5%8F%A6%E8%BE%9F%E8%B9%8A%E5%BE%84%E5%B1%8F%E8%94%BD%E6%88%AA%E5%9B%BE%E9%99%90%E5%88%B6/</link>
      <description>
        <![CDATA[<blockquote>
<p>先前有个项目代码需要做代码审计，因此将大部分代码打包成<code>library</code>调用了，包括<code>BaseActivity</code>,项目当时做等保，屏蔽了截图功能，目前需要恢复.</p>
</blockquote>
<h4 id="屏蔽截图"><a href="#屏蔽截图" class="headerlink" title="屏蔽截图"><]]>
      </description>
      <author>Logan</author>
      <category domain="https://log1992.cn/categories/android/">Android</category>
      <category domain="https://log1992.cn/tags/Activity/">Activity</category>
      <category domain="https://log1992.cn/tags/%E5%B1%8F%E8%94%BD%E6%88%AA%E5%9B%BE/">屏蔽截图</category>
      <pubDate>Wed, 31 May 2023 01:31:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>先前有个项目代码需要做代码审计，因此将大部分代码打包成<code>library</code>调用了，包括<code>BaseActivity</code>,项目当时做等保，屏蔽了截图功能，目前需要恢复.</p></blockquote><h4 id="屏蔽截图"><a href="#屏蔽截图" class="headerlink" title="屏蔽截图"></a>屏蔽截图</h4><p>首先讲如何屏蔽截图，一般只需要针对<code>Activity</code>设置Flag为<code>FLAG_SECURE</code>即可</p><p>我们选择在<code>BaseActivity</code>的<code>onCreate</code>方法中处理，这样所有继承的子类都实现了屏蔽截图.</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(<span class="meta">@Nullable</span> Bundle savedInstanceState)</span> &#123;</span><br><span class="line">       <span class="built_in">super</span>.onCreate(savedInstanceState);</span><br><span class="line">       getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>此时再截图就会提示“当前界面涉及隐私内容，不允许截屏”</p><h4 id="解除截屏限制"><a href="#解除截屏限制" class="headerlink" title="解除截屏限制"></a>解除截屏限制</h4><p>解除方法很简单，直接去掉上述Flag即可，但是目前的项目存在的问题是<code>BaseActivity</code>在<code>library</code>中，没有办法直接修改<code>aar</code>中的代码，或者说修改<code>aar</code>中的代码太麻烦了，那么有没有更简单的方法呢？答案是有的：</p><p>在我的<code>Application</code>中有用到这样一行代码</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">registerActivityLifecycleCallbacks(activityLifecycleCallbacks);</span><br></pre></td></tr></table></figure><p>注册了所有<code>activity</code>的生命周期监听，可以用来处理应用切换到前台后台的监听，做一些诸如判断网络状态重连等。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ActivityLifecycleCallbacks</span> &#123;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityCreated</span><span class="params">(Activity activity, Bundle savedInstanceState)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityStarted</span><span class="params">(Activity activity)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityResumed</span><span class="params">(Activity activity)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityPaused</span><span class="params">(Activity activity)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityStopped</span><span class="params">(Activity activity)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivitySaveInstanceState</span><span class="params">(Activity activity, Bundle outState)</span>;</span><br><span class="line">       <span class="keyword">void</span> <span class="title function_">onActivityDestroyed</span><span class="params">(Activity activity)</span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>既然如此，我们直接在<code>onActivityCreated</code>的时候处理不就行了吗？</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onActivityCreated</span><span class="params">(Activity activity, Bundle savedInstanceState)</span> &#123;</span><br><span class="line">            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>事实证明，并非如此。<code>onActivityCreated</code>确实会在<code>onCreate</code>之后调用，但是却是在<code>super.onCreate(savedInstanceState);</code>之后就已经调用了，我们的<code>addFlags</code>代码是在其后面写的，所以整个流程是</p><p>[onCreate的super.onCreate]{.label} </p><p>[onActivityCreated的clearFlags]{.label .warning} </p><p>[onCreate的addFlags]{.label .danger} </p><p>顺序不对，自然无法生效，所以我们只能在<code>onActivityStarted</code>中处理了</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">       <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onActivityStarted</span><span class="params">(Activity activity)</span> &#123;</span><br><span class="line">           <span class="comment">//无法编辑lib中的QFLibBaseActivity，直接在这里处理</span></span><br><span class="line">           activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);</span><br><span class="line">           <span class="keyword">if</span> (activityCount == <span class="number">0</span>) &#123;</span><br><span class="line">               isForeground = <span class="literal">true</span>;</span><br><span class="line">               INSLog.e(<span class="string">&quot;logan&quot;</span>, <span class="string">&quot;app回到前台,开始更新状态&quot;</span>);</span><br><span class="line">               <span class="type">Intent</span> <span class="variable">intent</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Intent</span>();</span><br><span class="line">               intent.setAction(QFMainActivity.ACTION_REFRESH_NETWORK_STATE);</span><br><span class="line">               sendBroadcast(intent);</span><br><span class="line">           &#125;</span><br><span class="line">           activityCount++;</span><br><span class="line">       &#125;</span><br></pre></td></tr></table></figure><p>然后我们就能正常截图了。</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
