<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>wawov Blog</title>
        <link>https://wawov.com/blog</link>
        <description>wawov Blog</description>
        <lastBuildDate>Fri, 12 Dec 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-Hans</language>
        <item>
            <title><![CDATA[如何处理音乐CUE文件]]></title>
            <link>https://wawov.com/blog/2025/12/12/handle-cue-file</link>
            <guid>https://wawov.com/blog/2025/12/12/handle-cue-file</guid>
            <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[最近在倒腾新买的nas，在音乐刮削用到这个开源项目music-tag-web,但是下载好的音乐文件就是无法进行音轨拆分。]]></description>
            <content:encoded><![CDATA[<p>最近在倒腾新买的nas，在音乐刮削用到这个开源项目<a href="https://github.com/xhongc/music-tag-web" target="_blank" rel="noopener noreferrer">music-tag-web</a>,但是下载好的音乐文件就是无法进行音轨拆分。</p>
<p>无论是把项目删除、数据清除，反复重新安装<a href="https://github.com/xhongc/music-tag-web" target="_blank" rel="noopener noreferrer">music-tag-web</a>都还是会保这样的错误。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202512121507382.png" alt="music-tag-web" class="img_ev3q"></p>
<p>扒拉扒拉源码也没有找到对应的地方，估计v1的版本也没有继续开源了。这下只能自己手动进行音轨拆分了。</p>
<p>这里需要用到几个工具：</p>
<ul>
<li>shntool</li>
<li>ffmpeg</li>
<li>iconv</li>
</ul>
<p>mac上安装这几个工具，可以使用brew</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">brew </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> shntool ffmpeg libiconv</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ffmpeg">ffmpeg<a href="https://wawov.com/blog/2025/12/12/handle-cue-file#ffmpeg" class="hash-link" aria-label="ffmpeg的直接链接" title="ffmpeg的直接链接">​</a></h2>
<p>如果音频是ape格式的，需要使用ffmpeg进行转换</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ffmpeg </span><span class="token parameter variable" style="color:#36acaa">-i</span><span class="token plain"> CDImage.ape CDImage.wav</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="shntool">shntool<a href="https://wawov.com/blog/2025/12/12/handle-cue-file#shntool" class="hash-link" aria-label="shntool的直接链接" title="shntool的直接链接">​</a></h2>
<p>根据cue文件切分音轨的工具</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">shntool </span><span class="token function" style="color:#d73a49">split</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> %t </span><span class="token parameter variable" style="color:#36acaa">-f</span><span class="token plain"> CDImage.cue </span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> wav CDImage.wav </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token builtin class-name">.</span><br></span></code></pre></div></div>
<p><code>-t</code> 参数表示分割出来的文件采用什么文件名，<code>%t</code> 表示用歌曲名字命名；<code>-f</code> 表示输入的 cue 文件；<code>-o</code> 指定输出格式；<code>-d</code> 参数为输出目录，此例用点表示当前目录。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="iconv">iconv<a href="https://wawov.com/blog/2025/12/12/handle-cue-file#iconv" class="hash-link" aria-label="iconv的直接链接" title="iconv的直接链接">​</a></h2>
<p>编码转换工具
大部分CUE文件都是在windows系统下生成的，所以需要将cue文件的编码转换为utf-8。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">iconv</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-f</span><span class="token plain"> gbk </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> utf8 CDImage.cue </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> CDImage-1.cue</span><br></span></code></pre></div></div>
<p><strong>ok，let' t fire～</strong></p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202512121519190.png" alt="result" class="img_ev3q"></p>]]></content:encoded>
            <category>NAS</category>
            <category>CUE文件</category>
        </item>
        <item>
            <title><![CDATA[Loki-管理日志从未如此简单]]></title>
            <link>https://wawov.com/blog/2025/03/17/grafana-loki</link>
            <guid>https://wawov.com/blog/2025/03/17/grafana-loki</guid>
            <pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[一个项目中，需要需要管理大量的日志，并且需要快速查询和分析日志。如果使用传统的日志管理方式，比如使用tail -f，或者使用grep，awk等命令，查询和分析日志会非常困难。如果使用ELK，需要部署大量的组件，并且需要花费大量的时间进行配置和优化。]]></description>
            <content:encoded><![CDATA[<p>一个项目中，需要需要管理大量的日志，并且需要快速查询和分析日志。如果使用传统的日志管理方式，比如使用<code>tail -f</code>，或者使用<code>grep</code>，<code>awk</code>等命令，查询和分析日志会非常困难。如果使用ELK，需要部署大量的组件，并且需要花费大量的时间进行配置和优化。</p>
<p>如果使用Grafana Loki，可以非常方便地管理日志，并且可以非常方便地查询和分析日志。而且系统资源占用小，部署简单。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="什么是loki">什么是Loki<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E4%BB%80%E4%B9%88%E6%98%AFloki" class="hash-link" aria-label="什么是Loki的直接链接" title="什么是Loki的直接链接">​</a></h2>
<p><a href="https://grafana.com/docs/loki/latest/" target="_blank" rel="noopener noreferrer">Loki 官方文档</a></p>
<p>Loki 是一个开源的日志管理系统，它使用 Prometheus 的存储格式来存储日志数据。Loki 的存储格式非常高效，可以存储大量的日志数据，并且可以非常方便地查询和分析日志。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="设计理念">设计理念<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E8%AE%BE%E8%AE%A1%E7%90%86%E5%BF%B5" class="hash-link" aria-label="设计理念的直接链接" title="设计理念的直接链接">​</a></h3>
<ul>
<li>简单性：Loki 的一个核心目标是简单。它不需要复杂的索引结构，而是将日志数据按流（stream）存储，以标签（labels）为基础进行查询。这种设计使得它的使用和配置相对容易。</li>
<li>无结构化：与大多数日志系统不同，Loki 不会对日志内容进行索引，而是将其按照时间戳和标签存储。这意味着用户可以更快地写入日志，而不需要担心复杂的索引管理。</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="配置文件">配置文件<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6" class="hash-link" aria-label="配置文件的直接链接" title="配置文件的直接链接">​</a></h3>
<p>创建一个<code>loki-config.yaml</code>文件为后续做准备，内容如下：</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">auth_enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">server</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">http_listen_port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">grpc_listen_port</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">9096</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">log_level</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> debug</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">grpc_server_max_concurrent_streams</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">common</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ring</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">instance_addr</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 0.0.0.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">kvstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> inmemory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">replication_factor</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">path_prefix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /tmp/loki</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">query_range</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">results_cache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">cache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">embedded_cache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">max_size_mb</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">limits_config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">allow_structured_metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">volume_enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">schema_config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">configs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">from</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token datetime number" style="color:#36acaa">2025-02-24</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tsdb</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">object_store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> filesystem</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">schema</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v13</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">index</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">prefix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> index_</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">period</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 24h</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">storage_config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">tsdb_shipper</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">active_index_directory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /tmp/loki/index</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">cache_location</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /tmp/loki/index_cache</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">filesystem</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">directory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /tmp/loki/chunks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">pattern_ingester</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="什么是alloy">什么是Alloy<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E4%BB%80%E4%B9%88%E6%98%AFalloy" class="hash-link" aria-label="什么是Alloy的直接链接" title="什么是Alloy的直接链接">​</a></h2>
<p>Grafana Alloy 是 Grafana Labs 开发的一款开源工具，旨在提升数据和监控的可视化体验。</p>
<p><a href="https://grafana.com/docs/alloy/latest/" target="_blank" rel="noopener noreferrer">Alloy 官方文档</a></p>
<blockquote>
<p>引用官方的一张图很容易理解Alloy能做什么</p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://grafana.com/media/docs/alloy/alloy_diagram_v2.svg" alt="Alloy 官方图" class="img_ev3q"></p>
<p>Alloy有多个组件，可以轻松支持从应用程序、数据库和OpenTelemetry收集数据，并将其转换为可查询的指标。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="配置文件-1">配置文件<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-1" class="hash-link" aria-label="配置文件的直接链接" title="配置文件的直接链接">​</a></h3>
<p>创建一个<code>config.alloy</code>文件为后续做准备，内容如下：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">discovery.docker "linux" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host = "unix:///var/run/docker.sock"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">discovery.relabel "containers" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	targets = []</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rule {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        source_labels = ["__meta_docker_container_label_enable_alloy"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        regex         = "^true$"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        action        = "keep"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	rule {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source_labels = ["__meta_docker_container_name"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		target_label  = "container_name"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	rule {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source_labels = ["__meta_docker_container_id"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		target_label  = "container_id"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loki.source.docker "default" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	host       = "unix:///var/run/docker.sock"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	targets    = discovery.docker.linux.targets</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	labels     = {"platform" = "docker"}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	relabel_rules = discovery.relabel.containers.rules</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	forward_to = [loki.write.local.receiver]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loki.write "local" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  endpoint {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    url = "http://loki:3100/loki/api/v1/push"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    batch_wait = "5s"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    batch_size = "1MiB"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    min_backoff_period = "1s" </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    max_backoff_period = "10s"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loki.process "gflog_fmt" {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.regex {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		expression = `^(?P&lt;timestamp&gt;\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[(?P&lt;level&gt;[A-Z]+)\] \{(?P&lt;request_id&gt;[a-f0-9]+)} \[(?P&lt;duration&gt;\d+ ms)\] (?P&lt;content&gt;.*)$`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.labels {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		values = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			timestamp = "timestamp",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			detected_level = "level",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			duration = "duration",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.replace {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		expression = "WARN"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		replace = "warning"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source = "detected_level"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.replace {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		expression = "ERRO"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		replace = "error"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source = "detected_level"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.replace {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		expression = "DEBU"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		replace = "debug"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source = "detected_level"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.timestamp {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source = "timestamp"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		format = "2006-01-02 15:04:05.000"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	stage.output {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		source = "content"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	forward_to = [loki.write.local.receiver]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>这里有几个关键点：</p>
<ul>
<li><code>discovery.docker</code>：用于发现Docker容器，并将其作为日志源。</li>
<li><code>loki.source.docker</code>：用于将Docker容器的日志写入Loki。</li>
<li><code>loki.process</code>：用于将日志格式化，并写入Loki。</li>
<li><code>loki.write</code>：用于将日志写入Loki。</li>
</ul>
<p>在<code>discovery.relabel</code>这个配置中加入了docker label过滤，只有<code>enable_alloy</code>为<code>true</code>的容器才会被采集。</p>
<p>在<code>loki.process "gflog_fmt"</code>中，使用了<a href="https://goframe.org/" target="_blank" rel="noopener noreferrer">GoFrame框架</a>，所以需要对日志的level进行处理，以满足Loki的level要求。</p>
<p>只有level格式一直的情况下，在Grafana中才能正确显示日志颜色。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202503172052055.png" alt="Grafana 日志颜色" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="部署">部署<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="部署的直接链接" title="部署的直接链接">​</a></h2>
<p>接下来使用DockerCompose部署Loki和Alloy。</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.8"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">loki</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> loki</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> grafana/loki</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">3.4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3100:3100"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./loki</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config.yaml</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/loki/loki</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config.file=/etc/loki/loki</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">alloy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> alloy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> grafana/alloy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/lib/docker/containers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/docker/containers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./config.alloy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/alloy/config.alloy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> run </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server.http.listen</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">addr=0.0.0.0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">12345 /etc/alloy/config.alloy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"12345:12345"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">depends_on</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> loki</span><br></span></code></pre></div></div>
<p>我这边的Grafana是独立部署的，我也个出一个参考的docker-compose.yaml文件，仅供参考。</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token string" style="color:#e3116c">'3'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">grafana</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  grafana/grafana</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">10.1.9</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">deploy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">limits</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">cpus</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'0.5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">memory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 2G</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  grafana</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  always</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./grafana.ini</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/grafana/grafana.ini</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./volumes/grafana</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/grafana</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">network_mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"host"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">expose</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3000"</span><br></span></code></pre></div></div>
<p>接下来一个命令<code>docker-compose up -d</code>一把梭服务正常就能起来了。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202503180928027.png" alt="Docker ps" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="配置grafana">配置Grafana<a href="https://wawov.com/blog/2025/03/17/grafana-loki#%E9%85%8D%E7%BD%AEgrafana" class="hash-link" aria-label="配置Grafana的直接链接" title="配置Grafana的直接链接">​</a></h2>
<p>打开Grafana，添加Loki数据源。路径: <code>connections</code> -&gt; <code>data sources</code> -&gt; <code>add data source</code> -&gt; <code>Loki</code>。</p>
<p>添加一个Dashboard，添加一个名为<code>Live logs</code>的panel。类型选择<code>Logs</code>，然后选择刚刚添加的<code>Loki</code>数据源。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202503180928027.png" alt="Grafana Live logs" class="img_ev3q"></p>]]></content:encoded>
            <category>Docker</category>
            <category>Grafana</category>
            <category>Loki</category>
            <category>Alloy</category>
        </item>
        <item>
            <title><![CDATA[了解HD Wallet]]></title>
            <link>https://wawov.com/blog/2025/03/10/understanding-hd-wallets</link>
            <guid>https://wawov.com/blog/2025/03/10/understanding-hd-wallets</guid>
            <pubDate>Mon, 10 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[数字钱包是进入加密货币世界的核心工具，本质是一套精密的密钥管理系统，而非简单的"存储容器"。]]></description>
            <content:encoded><![CDATA[<p>数字钱包是进入加密货币世界的核心工具，本质是一套精密的密钥管理系统，而非简单的"存储容器"。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="数字钱包">数字钱包<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#%E6%95%B0%E5%AD%97%E9%92%B1%E5%8C%85" class="hash-link" aria-label="数字钱包的直接链接" title="数字钱包的直接链接">​</a></h2>
<p>数字钱包是一种用于管理加密货币的工具，核心功能包括生成密钥对、存储资产、签署交易以及与区块链网络交互。其本质是密钥管理系统：</p>
<ul>
<li>​私钥：控制资产的唯一凭证（所有权证明）。</li>
<li>​公钥：生成接收地址，公开可见。</li>
<li>​地址：由公钥衍生（如Base58Check编码），用于接收资金。</li>
</ul>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="bip32分层确定性钱包hd-wallet">BIP32：分层确定性钱包（HD Wallet）<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#bip32%E5%88%86%E5%B1%82%E7%A1%AE%E5%AE%9A%E6%80%A7%E9%92%B1%E5%8C%85hd-wallet" class="hash-link" aria-label="BIP32：分层确定性钱包（HD Wallet）的直接链接" title="BIP32：分层确定性钱包（HD Wallet）的直接链接">​</a></h2>
<p><strong>原理</strong>
BIP32（Bitcoin Improvement Proposal 32）提出了一种分层确定性钱包（HD Wallet）​的结构，允许从单个种子（Seed）生成树状结构的密钥对。其核心思想是通过分层推导实现密钥的无限扩展，同时仅需备份一个主种子即可恢复所有密钥。</p>
<p><strong>关键流程：</strong></p>
<ul>
<li>​根种子生成：通过随机熵源生成一个根种子（通常为128-256位）。</li>
<li>​主密钥派生：使用HMAC-SHA512算法将根种子拆分为主私钥（m）和主链编码（Chain Code）。</li>
<li>子密钥派生：通过父私钥/公钥、链编码和索引号，逐层生成子密钥树。</li>
</ul>
<p><strong>特点：</strong></p>
<ul>
<li>​确定性：相同种子生成的密钥树完全一致。</li>
<li>单向性：子密钥无法推导父密钥或兄弟密钥。</li>
<li>强化衍生：索引号≥2³¹时使用父私钥推导，增强安全性。</li>
</ul>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="bip39助记词与种子生成">BIP39：助记词与种子生成<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#bip39%E5%8A%A9%E8%AE%B0%E8%AF%8D%E4%B8%8E%E7%A7%8D%E5%AD%90%E7%94%9F%E6%88%90" class="hash-link" aria-label="BIP39：助记词与种子生成的直接链接" title="BIP39：助记词与种子生成的直接链接">​</a></h2>
<p><strong>原理</strong>
BIP39通过助记词将随机熵转化为易备份的单词序列，并通过PBKDF2算法生成种子。</p>
<p><strong>生成步骤：</strong></p>
<ul>
<li>​熵生成：生成128/160/192/224/256位的随机熵。</li>
<li>校验和：取熵的SHA256哈希前n位（n=熵长度/32）作为校验和。</li>
<li>分割单词：将"熵+校验和"按11位分组，映射到2048个<a href="https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md" target="_blank" rel="noopener noreferrer">预定义单词</a>。</li>
<li>​种子生成：使用PBKDF2（助记词 + 盐值 "mnemonic"）生成512位种子</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="bip44多币种钱包路径规范">BIP44：多币种钱包路径规范<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#bip44%E5%A4%9A%E5%B8%81%E7%A7%8D%E9%92%B1%E5%8C%85%E8%B7%AF%E5%BE%84%E8%A7%84%E8%8C%83" class="hash-link" aria-label="BIP44：多币种钱包路径规范的直接链接" title="BIP44：多币种钱包路径规范的直接链接">​</a></h2>
<p><strong>原理</strong></p>
<p>BIP44基于BIP32定义了标准化的分层路径，支持多币种、多账户的钱包管理。</p>
<p><strong>路径结构：</strong></p>
<p><code>m / purpose' / coin_type' / account' / change / address_index</code></p>
<ul>
<li>​purpose'：固定为44（表示BIP44标准）。</li>
<li>coin_type'：币种标识（如0=比特币，60=以太坊）<a href="https://github.com/satoshilabs/slips/blob/master/slip-0044.md" target="_blank" rel="noopener noreferrer">完整的币种列表地址</a>。</li>
<li>account'：账户索引（从0开始）。</li>
<li>change：0=外部地址（收款），1=内部地址（找零）。</li>
<li>address_index：地址序号（从0递增）。</li>
</ul>
<p><strong>示例路径：</strong></p>
<ul>
<li>以太坊主账户第一个地址：<code>m/44'/60'/0'/0/0</code></li>
<li>比特币测试网第二个地址：<code>m/44'/1'/0'/0/1</code></li>
</ul>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="三者协作关系">三者协作关系<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#%E4%B8%89%E8%80%85%E5%8D%8F%E4%BD%9C%E5%85%B3%E7%B3%BB" class="hash-link" aria-label="三者协作关系的直接链接" title="三者协作关系的直接链接">​</a></h2>
<ul>
<li>​BIP39生成助记词，转化为种子。</li>
<li>BIP32用种子生成主密钥和树状子密钥。</li>
<li>BIP44定义路径规则，实现多币种、多账户管理。</li>
</ul>
<p><strong>优势总结：</strong></p>
<ul>
<li>易备份：仅需保存助记词或种子。</li>
<li>​安全性：种子冷存储，子密钥可热生成。</li>
<li>​标准化：跨钱包兼容（如MetaMask、Ledger）</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="写到最后">写到最后<a href="https://wawov.com/blog/2025/03/10/understanding-hd-wallets#%E5%86%99%E5%88%B0%E6%9C%80%E5%90%8E" class="hash-link" aria-label="写到最后的直接链接" title="写到最后的直接链接">​</a></h2>
<p>理解完原理，进行实操才是王道。</p>
<p>为此，我开发了一个简单的网页应用，用于生成和展示HD Wallet的密钥。</p>
<p>可以访问<a href="https://cryptohub.lol/tools/hd-wallet" target="_blank" rel="noopener noreferrer">https://cryptohub.lol/tools/hd-wallet</a>进行体验。</p>]]></content:encoded>
            <category>BIP32</category>
            <category>BIP44</category>
            <category>BIP39</category>
            <category>HD Wallet</category>
        </item>
        <item>
            <title><![CDATA[使用Cloudflare CDN加速博客资源]]></title>
            <link>https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare</link>
            <guid>https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare</guid>
            <pubDate>Fri, 13 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Cloudflare是一个广泛使用的CDN服务，它不仅可以加速博客资源，还能提供额外的安全保护。本文将介绍如何使用Cloudflare CDN加速你的博客及其好处。]]></description>
            <content:encoded><![CDATA[<p>Cloudflare是一个广泛使用的CDN服务，它不仅可以加速博客资源，还能提供额外的安全保护。本文将介绍如何使用Cloudflare CDN加速你的博客及其好处。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="什么是cdn">什么是CDN？<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E4%BB%80%E4%B9%88%E6%98%AFcdn" class="hash-link" aria-label="什么是CDN？的直接链接" title="什么是CDN？的直接链接">​</a></h2>
<p>内容分发网络（CDN）是一组分布在多个地理位置的服务器，通过将内容缓存到离用户更近的位置，来提高网站的加载速度。CDN通过减少用户与服务器之间的距离，确保用户能够快速加载网页和资源。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么使用cloudflare-cdn">为什么使用Cloudflare CDN？<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8cloudflare-cdn" class="hash-link" aria-label="为什么使用Cloudflare CDN？的直接链接" title="为什么使用Cloudflare CDN？的直接链接">​</a></h2>
<ul>
<li>全球覆盖：Cloudflare在多个国家和地区拥有数据中心，能够为全球用户提供快速的访问速度。</li>
<li>安全性：Cloudflare提供防火墙、DDoS防护和SSL加密等安全功能，保护你的博客免受网络攻击。</li>
<li>易于使用：Cloudflare的设置过程简单明了，即使是初学者也能轻松上手。</li>
<li>免费计划：Cloudflare提供免费的基础服务，非常适合个人博客和小型网站。</li>
</ul>
<p>对于白嫖党来说，Cloudflare的免费计划，足够可以满足大部分出场景需求。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="配置cdn设置">配置CDN设置<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E9%85%8D%E7%BD%AEcdn%E8%AE%BE%E7%BD%AE" class="hash-link" aria-label="配置CDN设置的直接链接" title="配置CDN设置的直接链接">​</a></h2>
<ul>
<li>首先设置一个cdn域名，比如我这里设置一个<code>cdn.wawov.com</code>，CNAME到<code>cdn.jsdelivr.net</code>。
这里有小明就会问，这域名在国内不是访问不了吗？先别急，后面会解决。</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409161508989.png" alt="CDN设置" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="重定向规则">重定向规则<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E9%87%8D%E5%AE%9A%E5%90%91%E8%A7%84%E5%88%99" class="hash-link" aria-label="重定向规则的直接链接" title="重定向规则的直接链接">​</a></h2>
<ul>
<li>接下来我们去配置一下重定向规则，点击<code>规则</code> -&gt; <code>重定向规则</code> -&gt; <code>创建规则</code></li>
</ul>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409161510350.png" alt="重定向规则" class="img_ev3q"></p>
<p>这里需要简单配置两个规则就可以解决国内访问不了的问题。</p>
<ul>
<li><code>(http.host eq "cdn.wawov.com" and ip.geoip.country ne "CN")</code></li>
<li><code>(http.host eq "cdn.wawov.com" and ip.geoip.country eq "CN")</code></li>
</ul>
<p>第一个规则，当访问的ip不是中国大陆的时候，重定向到<code>https://cdn.jsdelivr.net</code></p>
<p>第二个规则，当访问的ip是中国大陆的时候，重定向到<code>https://cdn.jsdmirror.com</code></p>
<p>具体配置可以参考下图：</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409170856859.png" alt="cdn_cn" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409170858236.png" alt="cdn_abord" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="验证配置">验证配置<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E9%AA%8C%E8%AF%81%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="验证配置的直接链接" title="验证配置的直接链接">​</a></h2>
<p>通过代理访问具体资源，验证是否生效。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409171113832.png" alt="无代理访问" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409171023687.png" alt="代理访问" class="img_ev3q"></p>
<p>可以看到在Cloudflare设置的规则转发已经生效。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a href="https://wawov.com/blog/2024/09/13/blog-asset-use-cloudflare#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2>
<p>通过使用Cloudflare CDN，与规则设置，这样就可以在博客使用一个统一的域名进行定义资源。希望本文对你有所帮助，让你能够更好地利用Cloudflare CDN加速你的博客。</p>]]></content:encoded>
            <category>Cloudflare</category>
            <category>CDN</category>
        </item>
        <item>
            <title><![CDATA[Bye Travis , Hello Github Action]]></title>
            <link>https://wawov.com/blog/2019/10/10/github-action</link>
            <guid>https://wawov.com/blog/2019/10/10/github-action</guid>
            <pubDate>Thu, 10 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[最近折腾了一下Github的Action，发现还挺好用的。那就折腾到底吧，把Travis的构建Blog的CI迁移到Github的Action。毕竟有时间打开Travis还挺慢的。]]></description>
            <content:encoded><![CDATA[<p>最近折腾了一下Github的Action，发现还挺好用的。那就折腾到底吧，把Travis的构建Blog的CI迁移到Github的Action。毕竟有时间打开Travis还挺慢的。</p>
<p>先理一下整体流程</p>
<ol>
<li>npm install</li>
<li>hexo g</li>
<li>进入public目录push到对应的仓库</li>
</ol>
<p>创建一个Action，默认的会在当前仓库下<code>.github/workflows</code>目录下创建一个xxx.yml文件。这个Workflow的语法主干有几个元素<code>name</code>、<code>on</code>、<code>env</code>、<code>jobs</code></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">workflows的名字</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">name: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/** </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">如何触发当前workflow,多个触发条件可以使用数组的方式，例如：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">on: [push,pull_request]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">on: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">环境变量，以key-valued方式</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">env:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">一个workflow由一个或者多个job组成，job默认都是并行运行的，如果有job之间的依赖可以使用jobs.&lt;job_id&gt;.needs关键字。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">每一个job可以必须使用关键字jobs.&lt;job_id&gt;.runs-on制定运行环境。可用的环境目前有：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ubuntu-latest, ubuntu-18.04, or ubuntu-16.04</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">windows-latest, windows-2019, or windows-2016</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">macOS-latest or macOS-10.14</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">jobs</span><br></span></code></pre></div></div>
<p>按照之前整理的流程可以写出一个workflows:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">name: DeployBlog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">on:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  push:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    branches: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     - master</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    paths: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - '!.github/**'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">env:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GH_REF: github.com/ledboot/ledboot.github.io.git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  GitHub_Token: ${{ secrets.token }}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">jobs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  build:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    runs-on: ubuntu-latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    steps:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - uses: actions/checkout@master</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: npm install &amp; hexo g</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      run: |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        npm install</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ./node_modules/.bin/hexo g</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: publish</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      run: |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if [ -d 'public' ];then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          cd ./public</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git init</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git config user.name "xxx"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git config user.email "xxx@gmail.com"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git add .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git commit -m "Update docs"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          git push --force --quiet "https://${GitHub_Token}@${GH_REF}" master:master</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        fi</span><br></span></code></pre></div></div>
<p>这里添加了一些细节处理：</p>
<ul>
<li>在<code>on</code>中指定触发的分支，并且忽略<code>.github</code>目录，因为我编辑workflow文件，提交的时候，并不想触发构建。</li>
<li>添加<code>secrets</code>，可以在Setting Tab页中添加，引用方式{<code>${{secrets.xxxx}}</code>}</li>
</ul>]]></content:encoded>
            <category>Github</category>
        </item>
        <item>
            <title><![CDATA[CKA之路]]></title>
            <link>https://wawov.com/blog/2019/09/22/cka</link>
            <guid>https://wawov.com/blog/2019/09/22/cka</guid>
            <pubDate>Sun, 22 Sep 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[接触kubernetes已经有2年多了，这段时间里云原生的概念慢慢被普及，许多公司纷纷走上了上云的路上，使用kubernetes去管理生产上面的应用，kubernetes的使用正在爆炸式地增长。Linux基金会和云原生计算基金会（CNCF）为帮助开发Kubernetes生态系统创建了Certified Kubernetes Administrator简称CKA。]]></description>
            <content:encoded><![CDATA[<p>接触kubernetes已经有2年多了，这段时间里云原生的概念慢慢被普及，许多公司纷纷走上了上云的路上，使用kubernetes去管理生产上面的应用，kubernetes的使用正在爆炸式地增长。Linux基金会和云原生计算基金会（CNCF）为帮助开发Kubernetes生态系统创建了<code>Certified Kubernetes Administrator</code>简称CKA。</p>
<p>刚开始知道这个认证的时候，也没有想过去考，而且但是考的话，国内是没有考场的，只能用自己的电脑科学上网打开web terminal，延迟应该挺厉害的。</p>
<p>直到今年国内才有考场试运行CKA考试。第一阶段的试运行，我知道的时候已经过时了，但是也还是没有想考的想法。时间来到8月，第二阶段的试运行来了，纠结了几天，结果还是按下了支付的按钮。看了一下那些基础课程对我没有什么用，毕竟我是有基础的嘛，搞了2年也不是白混的啦。看了一下时间要在9月30号之前完成考试。接下来的日子可能要熬熬夜了。</p>
<p>接下来我在网上搜了一圈，发现有些不错的视频</p>
<ul>
<li><a href="https://www.bilibili.com/video/av40743487" target="_blank" rel="noopener noreferrer">B站视频1</a></li>
<li><a href="https://www.bilibili.com/video/av49387629" target="_blank" rel="noopener noreferrer">B站视频2</a></li>
</ul>
<p>看完2个视频，我已经飘了，感觉可以去考试了。因为发现这个认证难度其实并不大，主要是熟练一些命令，深刻了解kubernetes的运作机制。对于已经接触2年kubernetes的我而言，问题已经不大。现在主要就是练速度了，毕竟考试时间是3个小时。</p>
<p>接下来我在网上搜索了一些CKA的考题，不断的练习就是了。下面链接是我为知笔记记录的题目：</p>
<p><a href="http://679a62b5.wiz03.com/share/s/1DCCaR2Jf4ZG2Iud1x3tANNB2h4vFU2xCQug28LBsD0UBPEO" target="_blank" rel="noopener noreferrer">CKA题库</a></p>
<p>考试小技巧：</p>
<ul>
<li>考试过程可以打开一个记事本，可以把一些常用的模版复制到这里来，便于后续题目修改编辑，快速创建。</li>
<li>熟悉文档<a href="https://kubernetes.io/docs/home/" target="_blank" rel="noopener noreferrer">https://kubernetes.io/docs/home/</a> 毕竟很多东西，考试的时候忘记了，也可以在这里搜索出来。</li>
<li>有时候看中文题目可能翻译不太准确，切换英文的看一次，确保自己理解与实际题目一致。</li>
</ul>
<p>在考试前几天，我发现了一个网站，这个网站简直了，不过是全英文，而且不带字幕的，是一个国外的学习网站，这个网站可以秒杀上面2个视频。我看中它的是有模拟考试，我的天！二话不说，直接支付35刀。前提是你要有一张外币信用卡噢。</p>
<p><a href="https://kodekloud.com/courses/enrolled/539883" target="_blank" rel="noopener noreferrer">KodeKloud</a></p>
<p>如果你在10月5号看到这篇文章。发邮件(<a href="mailto:popmusicbbq@126.com" target="_blank" rel="noopener noreferrer">popmusicbbq@126.com</a>)给我，友情分享账号。毕竟我只买了一个月。</p>
<p>意外总会有的，我考试的时候网页502，还有3题没有做，​我也是服了。都打算申请重考了。果然试运行就是这样呀。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409101703290.JPG" alt="考试结果" class="img_ev3q"></p>
<p>可是实力不允许呀，过了几天居然我收到了通过的邮件，Surprise！</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset/img/2024/202409101703764.JPG" alt="通过邮件" class="img_ev3q"></p>]]></content:encoded>
            <category>CKA</category>
            <category>Kubernetes</category>
        </item>
        <item>
            <title><![CDATA[TLS Bootstrapping Worker Nodes]]></title>
            <link>https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes</link>
            <guid>https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes</guid>
            <pubDate>Sun, 15 Sep 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[step 1]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-1">step 1<a href="https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes#step-1" class="hash-link" aria-label="step 1的直接链接" title="step 1的直接链接">​</a></h2>
<p>在<code>kube-system</code>namespace下创建一个secret,名字格式:bootstrap-token-&lt;token&gt;</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &gt; bootstrap-token-05832d.yaml &lt;&lt; EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: bootstrap-token-05832d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">type: bootstrap.kubernetes.io/token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">stringData:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  token-id: 05832d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  token-secret: x262bbbe835dx21k</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  usage-bootstrap-authentication: "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  usage-bootstrap-signing: "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  auth-extra-groups: system:bootstrappers:node03</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-2">step 2<a href="https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes#step-2" class="hash-link" aria-label="step 2的直接链接" title="step 2的直接链接">​</a></h2>
<p>授权节点创建CSR</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create clusterrolebinding crb-bootstrappers --clusterrole=system:node-bootstrapper --group=system:bootstrappers</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-3">step 3<a href="https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes#step-3" class="hash-link" aria-label="step 3的直接链接" title="step 3的直接链接">​</a></h2>
<p>创建bootstrap-kubeconfig</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://172.17.0.77:6443' --certificate-authority=/etc/kubernetes/pki/ca.crt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=05832d.x262bbbe835dx21k</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-4">step 4<a href="https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes#step-4" class="hash-link" aria-label="step 4的直接链接" title="step 4的直接链接">​</a></h2>
<p>配置kubelet.service，注意路径在<code>/etc/systemd/system/kubelet.service</code></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[Unit]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Description=Kubernetes Kubelet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Documentation=https://github.com/kubernetes/kubernetes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[Service]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ExecStart=/usr/bin/kubelet \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> --bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> --kubeconfig=/var/lib/kubelet/kubeconfig \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> --register-node=true \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> --v=2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Restart=on-failure</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">StandardOutput=file:/var/kubeletlog1.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">StandardError=file:/var/kubeletlog2.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RestartSec=5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[Install]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">WantedBy=multi-user.target</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>配置好kubelet.service之后：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">systemctl daemon-reload</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">systemctl enable kubelet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">systemctl start kubelet</span><br></span></code></pre></div></div>
<p>kubelet正常启动之后，在master节点上<code>kubectl get csr</code>,可以看到一个pending状态的csr,可以通过创建一个clusterrolebinding让csr自动approve csr。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-5">step 5<a href="https://wawov.com/blog/2019/09/15/tls-bootstrapping-worker-nodes#step-5" class="hash-link" aria-label="step 5的直接链接" title="step 5的直接链接">​</a></h2>
<p>自动approve csr</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create clusterrolebinding crb-node-autoapprove-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --group=system:bootstrappers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>证书过期自动续签</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create clusterrolebinding crb-node-autorotate-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes</span><br></span></code></pre></div></div>
<p>Done !</p>]]></content:encoded>
            <category>CKA</category>
            <category>Kubernetes</category>
        </item>
        <item>
            <title><![CDATA[protocol buffer 使用]]></title>
            <link>https://wawov.com/blog/2016/12/08/protocol-buffer-use</link>
            <guid>https://wawov.com/blog/2016/12/08/protocol-buffer-use</guid>
            <pubDate>Thu, 08 Dec 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[安装]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_LWe7" id="安装">安装<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E5%AE%89%E8%A3%85" class="hash-link" aria-label="安装的直接链接" title="安装的直接链接">​</a></h2>
<p>mac上使用homebrew安装，</p><pre>brew install protobuf</pre><p></p>
<p>其他os，可以到<a href="http://code.google.com/p/protobuf/downloads/list" target="_blank" rel="noopener noreferrer">官网</a>下载。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="编写proto文件">编写.proto文件<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E7%BC%96%E5%86%99proto%E6%96%87%E4%BB%B6" class="hash-link" aria-label="编写.proto文件的直接链接" title="编写.proto文件的直接链接">​</a></h2>
<p>列子:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">package pingan.kanyun.sdk;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">option java_package = "com.pingan.kanyun.sdk.proto";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message DeviceInfo{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string os_version = 1;//OS版本,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string app_version = 2;//APP版本,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string imei_meid = 3;//机器码（IMEI/MEID）,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string bandName = 4;//客户端品牌信息</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string model = 5;//手机型号</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string cpuModel = 6;//CPU型号</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string cpuInstructionSet = 7;//CPU指令集</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string cpuHardware = 8;//CPU厂商</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional bool isRoot = 9;//是否越狱</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string displaySize = 10;//屏幕分辨率</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	optional string language = 11;//语言</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    optional int32 result_per_page ＝ 12;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    repeated int32 samples = 13 [packed=true];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    enum PhoneType{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    	MOBILE = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    	HOME = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    	WORK = 2;      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>message定义了11个field，每个field都有名称与类型组成。</p>
<ul>
<li>指定field的类型</li>
</ul>
<p>可以是基本类型：string int32，也可以是指定的复杂类型属性，包括枚举和其他类型</p>
<ul>
<li>分配标签</li>
</ul>
<p>每一个field都是唯一数字的标记，<em>这是用来标记这个field在message二进制格式中的位置，一旦使用就不能在修改顺序</em></p>
<p><em>ps:</em></p>
<ul>
<li>
<p>标记从1-15只有一个字节编码，包括自增长属性</p>
</li>
<li>
<p>标记从16-2047占用两个字节。因此尽量频繁使用1-15，记住为未来的扩展留下一些位置。</p>
</li>
<li>
<p>最小的tag你可以定义为1，最大2的29次方-1  536870922.你同样不能使用19000-19999（这个位置已经被Google PB实现）</p>
</li>
<li>
<p>由于历史原因，repeated字段如果是基本数字类型的话，不能有效地编码。现在代码可以使用特殊选项[packed=true]来得到更有效率的编码。</p>
</li>
</ul>
<p>如果字段的属性值是固定的几个值，可以使用枚举</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="enumerations">Enumerations<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#enumerations" class="hash-link" aria-label="Enumerations的直接链接" title="Enumerations的直接链接">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message SearchRequest {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required string query = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional int32 page_number = 2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional int32 result_per_page = 3 [default = 10];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  enum Corpus {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    UNIVERSAL = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    WEB = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    IMAGES = 2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    LOCAL = 3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    NEWS = 4;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    PRODUCTS = 5;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    VIDEO = 6;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional Corpus corpus = 4 [default = WEB];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="自定义消息类型">自定义消息类型<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B" class="hash-link" aria-label="自定义消息类型的直接链接" title="自定义消息类型的直接链接">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message SearchResponse {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  repeated Result result = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message Result {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  required string url = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional string title = 2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  repeated string snippets = 3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="import-定义">import 定义<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#import-%E5%AE%9A%E4%B9%89" class="hash-link" aria-label="import 定义的直接链接" title="import 定义的直接链接">​</a></h3>
<p>使用import关键字，可以引用另外一个.proto文件</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import "myproject/other_protos.proto";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="内部类">内部类<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E5%86%85%E9%83%A8%E7%B1%BB" class="hash-link" aria-label="内部类的直接链接" title="内部类的直接链接">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message SearchResponse {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  message Result {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    required string url = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    optional string title = 2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    repeated string snippets = 3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  repeated Result result = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>引用内部类是，使用parent.type，例如：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message SomeOtherMessage {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional SearchResponse.Result result = 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="extentions">Extentions<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#extentions" class="hash-link" aria-label="Extentions的直接链接" title="Extentions的直接链接">​</a></h3>
<p>extensions 声明一个消息中的一定范围的field的顺序数字用于进行扩展。其它人可以在自己的.proto文件中重新定义这些消息field，而不需要去修改原始的.proto文件</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message Foo{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    extentions 100 to 199;//这说明100～199是保留的。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>在新的fields添加Foo</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">extend Foo {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional int32 bar = 126;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="内嵌的extensions">内嵌的extensions<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E5%86%85%E5%B5%8C%E7%9A%84extensions" class="hash-link" aria-label="内嵌的extensions的直接链接" title="内嵌的extensions的直接链接">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message Baz {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  extend Foo {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    optional int32 bar = 126;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="选择extension-顺序数字">选择Extension 顺序数字<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E9%80%89%E6%8B%A9extension-%E9%A1%BA%E5%BA%8F%E6%95%B0%E5%AD%97" class="hash-link" aria-label="选择Extension 顺序数字的直接链接" title="选择Extension 顺序数字的直接链接">​</a></h3>
<p>非常重要的一点是双方不能使用同样数字添加一样的message类型，这样extension会被解释为错误类型。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="packages">Packages<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#packages" class="hash-link" aria-label="Packages的直接链接" title="Packages的直接链接">​</a></h3>
<p>可以给一个.protol文件增加一个optional的package描述，来保证message尽量不会出现名字相同的重名。</p>
<p>package会根据选择的语言来生成不同的代码。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">package foo.bar;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message Open { </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>package会根据选择的语言来生成不同的代码：</p>
<ul>
<li>C++</li>
</ul>
<p>生成的classes是用C++的namespace来区分的。举例：Open would be in the namespace foo::bar。</p>
<ul>
<li>Java</li>
</ul>
<p>package用于Java的package，除非你单独的指定一个option java_package 在.proto文件中。</p>
<ul>
<li>Python</li>
</ul>
<p>package是被忽略的，因为Python的modules是通过它们的文件位置来组织的。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="options">options<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#options" class="hash-link" aria-label="options的直接链接" title="options的直接链接">​</a></h3>
<p>在一个proto文件中，还可以存在一些options。options不能改变一个声明的整体的意义，但是可以影响一定的上下文。</p>
<ol>
<li>
<p>一些options是第一级的，意味着它们应该被写在顶级范围，而不是在任何message,enum，sercie的定义中。</p>
</li>
<li>
<p>一些options是message级别的，意味着它们应该被写入message的描述中。</p>
</li>
<li>
<p>一些options是field-level级别的，意味着它们应该被写入field的描述中，options也可以被写入enum类型中，enum的值，service类型 和service方法。</p>
</li>
</ol>
<p>常用的options：</p>
<ul>
<li><strong>java_package (file option)</strong></li>
</ul>
<p>定义生成的java class的package。如果在proto文件中没有明确的java_package选项，那么默认会使用package关键字指定的package名。</p>
<p>但是proto package通常不会好于Java packages，因为proto packages通常不会以domain名称开始。</p>
<p>如果不生成java代码，此选项没有任何影响。</p>
<p>例子：</p><pre>option java_package = "com.example.foo";</pre><p></p>
<ul>
<li><strong>java_outer_classname (file option)</strong></li>
</ul>
<p>指定想要生成的class名称，如果此参数没有指定的话，那么默认使用.proto文件名来做为类名，并且采用驼峰表示（比如：foo_bar.proto 为 FooBar.java）</p>
<p>如果不生成java代码，此选项没有影响。</p>
<p>例子：</p><pre>option java_outer_classname = "Ponycopter";</pre><p></p>
<ul>
<li><strong>optimize_for (file option)</strong></li>
</ul>
<p>可以设置为speed、code_size或者lite_runtime。</p>
<ol>
<li>
<p>SPEED:默认。protocol编译器会生成classes代码，提供了message类的序列化、转换和其它通用操作。这个代码是被高度优化过的。</p>
</li>
<li>
<p>CODE_SIZE: protocol编译器会生成最小的classes，并且依赖共享、基于反射的代码实现序列化、转换和其它通用操作。生成的classes代码小于speed，但是操作会慢一点。classes会实现跟SPEED模式一样的公共API。这个模式通常用在一个应用程序包含了大量的proto文件，但是并不需要所有的代码都执行得很快</p>
</li>
<li>
<p>LITE_RUNTIME: protocol编译器会生成仅仅依赖 lite 运行库（libprotobuf-lite代替libprotobuf）。lite运行时比全量库小很多，省略了某种特性（如： descriptors and reflection）这个选项对于运行在像移动手机这种有约束平台上的应用更有效。 编译器仍然会对所有方法生成非常快的代码实现，就像SPEED模式一样。protocol编译器会用各种语言来实现MessageList接口，但是这个接口仅仅提供了其它模式实现的Message接口的一部分方法子集。</p>
</li>
</ol>
<p>例子：</p><pre>option optimize_for = CODE_SIZE;</pre><p></p>
<ul>
<li><strong>cc_generic_services, java_generic_services, py_generic_services (file options)</strong></li>
</ul>
<p>无论如何，protoc编译器会生成基于C++,Java,Python的抽象service代码，这些默认都是true。截至到2.3.0版本，RPC实现提供了代码生成插件去生成代码，不再使用抽象类。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">option cc_generic_services = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">option java_generic_services = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">option py_generic_services = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<ul>
<li><strong>message_set_wire_format (message option)</strong></li>
</ul>
<p>如果设置为true，消息使用不同的二进制格式来兼容谷歌内部使用的称为MessageSet的旧格式。用户在google以外使用，将不再需要使用这个option。</p>
<p>消息必须按照以下声明：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message Foo {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  option message_set_wire_format = true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  extensions 4 to max;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<ul>
<li><strong>自定义options</strong></li>
</ul>
<p>protocol buffer还允许你自定义options。这是个高级特性，大多数人并不需要。options其实都定义在 google/protobuf/descriptor.proto文件中。</p>
<p>自定义的options是简单的，继承这些messages：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import "google/protobuf/descriptor.proto";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">extend google.protobuf.MessageOptions {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  optional string my_option = 51234;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">message MyMessage {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  option (my_option) = "Hello world!";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>这里我们定义了一个message级别的消息选项，当使用这个options的时候，选项的名称必须用括号括起来，以表明它是一个extension。</p>
<p>我们在C++中读取my_option的值就像下面这样：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">string value = MyMessage::descriptor()-&gt;options().GetExtension(my_option);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>在Java中：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">String value = MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<ul>
<li><strong>生成class代码</strong></li>
</ul>
<p>为了生成java、python、C++代码，你需要运行protoc编译器 protoc 编译.proto文件。编译器运行命令：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p><em><strong>使用.代表当前目录。</strong></em></p>
<p>import_path 查找proto文件的目录，如果省略的话，就是当前目录。存在多个引入目录的话，可以使用--proto_path参数来多次指定，</p>
<p>-I=IMPORT_PATH就是--proto_path的缩写</p>
<p>输出目录</p>
<ul>
<li>
<p>--cpp_out       生成C++代码在DST_DIR目录</p>
</li>
<li>
<p>--java_out      生成Java代码在DST_DIR目录</p>
</li>
<li>
<p>--python_out    生成Python代码在DST_DIR目录</p>
</li>
</ul>
<p>有个额外的好处，如果DST是.zip或者.jar结尾，那么编译器将会按照给定名字输入到一个zip压缩格式的文件中。</p>
<p>输出到.jar会有一个jar指定的manifest文件。注意 如果输出文件已经存在，它将会被覆盖；编译器的智能不足以自动添加文件到一个存在的压缩文件中。</p>
<p>你必须提供一个或者多个.proto文件用作输入。虽然文件命名关联到当前路径，每个文件必须在import_path路径中一边编译器能规定它的规范名称。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="更新message">更新message<a href="https://wawov.com/blog/2016/12/08/protocol-buffer-use#%E6%9B%B4%E6%96%B0message" class="hash-link" aria-label="更新message的直接链接" title="更新message的直接链接">​</a></h3>
<p>如果一个message 不再满足所有需要，需要对字段进行调整。(举例：对message增加一个额外的字段，但是仍然有支持旧格式message的代码在运行)</p>
<p>要注意以下几点：</p>
<ol>
<li>
<p>不要修改已经存在字段的数字顺序标示</p>
</li>
<li>
<p>可以增加optional或者repeated的新字段。这么做以后，所有通过旧格式message序列化的数据都可以通过新代码来生成对应的对象，正如他们不会丢失任何required元素。你应该为这些元素添加合理的默认值，以便新代码可以与旧代码生成的消息交互。 新代码创建的消息中旧代码不存在的字段，在解析的时候，旧代码会忽略掉新增的字段。无论如何，未知的field不会被丢弃，注意未知field对于Python来说当前不可用。</p>
</li>
<li>
<p>非required字段都可以转为extension ，反之亦然，只要type和number保持不变。</p>
</li>
<li>
<p>int32, uint32, int64, uint64, and bool 是全兼容的。这意味着你能改变一个field从这些类型中的一个改变为另一个，而不用考虑会打破向前、向后兼容性。如果一个数字是通过网络传输而来的相应类型转换，你将会遇到type在C++中遇到的问题。（e.g. if a 64-bit number is read as an int32, it will be truncated to 32 bits）</p>
</li>
<li>
<p>sint32 and sint64 彼此兼容,但是不能兼容其它integer类型。</p>
</li>
<li>
<p>string and bytes 在UTF-8编码下是兼容的。</p>
</li>
<li>
<p>如果bytes包含一个message的编码,内嵌message与bytes兼容。</p>
</li>
<li>
<p>fixed32 兼容 sfixed32,  fixed64 兼容 sfixed64。</p>
</li>
<li>
<p>optional 兼容 repeated。用一个repeat字段的编码结果作为输入，认为这个字段是可选择的客户端会这样处理,如果是原始类型的话，获得最后的输入作为相应的option值；如果是message 类型，合并所有输入元素。</p>
</li>
<li>
<p>更改默认值通常是OK的。要记得默认值并不会通过网络发送，如果一个程序接受一个特定字段没有设置值的消息，应用将会使用自己的版本协议定义的默认值，不会看见发送者的默认值。</p>
</li>
</ol>]]></content:encoded>
            <category>protocol buffer</category>
        </item>
        <item>
            <title><![CDATA[ASM Bytecode Framework探索与使用]]></title>
            <link>https://wawov.com/blog/2016/06/22/asm-bytecode-framework</link>
            <guid>https://wawov.com/blog/2016/06/22/asm-bytecode-framework</guid>
            <pubDate>Wed, 22 Jun 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[ASM_cover.png) -->]]></description>
            <content:encoded><![CDATA[<p>SM_cover.png) --&gt;</p>
<p>ASM是一款基于java字节码层面的代码分析和修改工具。无需提供源代码即可对应用嵌入所需debug代码，用于应用API性能分析。ASM可以直接产生二进制class文件，也可以在类被加入JVM之前动态修改类行为。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="asm库的结构">ASM库的结构<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#asm%E5%BA%93%E7%9A%84%E7%BB%93%E6%9E%84" class="hash-link" aria-label="ASM库的结构的直接链接" title="ASM库的结构的直接链接">​</a></h5>
<p>.png) --&gt;</p>
<ul>
<li><strong>Core</strong> 为其他包提供基础的读、写、转化Java字节码和定义的API，并且可以生成Java字节码和实现大部分字节码的转换</li>
<li><strong>Tree</strong>提供了Java字节码在内存中的表现</li>
<li><strong>Analysis</strong>为存储在tree包结构中的java方法字节码提供基本的数据流统计和类型检查算法</li>
<li><strong>Commons</strong>提供一些常用的简化字节码生成转化和适配器</li>
<li><strong>Util</strong>包含一些帮助类和简单的字节码修改，有利于在开发或者测试中使用</li>
<li><strong>XML</strong>提供一个适配器将XML和SAX-comliant转化成字节码结构，可以允许使用XSLT去定义字节码转化。</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="class文件结构">class文件结构<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#class%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84" class="hash-link" aria-label="class文件结构的直接链接" title="class文件结构的直接链接">​</a></h5>
<p>在了解ASM之前，有必要先了解一下class文件结构。对于每个class文件其实都是有固定的结构信息，而且保留了源码文件中的符号。下图是class文件的格式图。其中带 * 号的表示可重复的结构。</p>
<p>.png)</p>
<ul>
<li>类结构体中所有的修饰符、字符常量和其他常量都被存储在class文件开始的一个常量堆栈(Constant Stack)中，其他结构体通过索引引用。</li>
<li>每个类必须包含headers（包括：class name, super class, interface, etc.）和常量堆栈（Constant Stack）其他元素，例如：字段（fields）、方法（methods）和全部属性（attributes）可以选择显示或者不显示。</li>
<li>每个字段块（Field section）包括名称、修饰符（public, private, etc.）、描述符号(descriptor)和字段属性。</li>
<li>每个方法区域（Method section）里面的信息与header部分的信息类似，信息关于最大堆栈（max stack）和最大本地变量数量（max local variable numbers）被用于修改字节码。对于非abstract和非native的方法有一个方法指令表，exceptions表和代码属性表。除此之外，还可以有其他方法属性。</li>
<li>每个类、字段、方法和方法代码的属性有属于自己的名称记录在类文件格式的JVM规范的部分，这些属性展示了字节码多方面的信息，例如源文件名、内部类、签名、代码行数、本地变量表和注释。JVM规范允许定义自定义属性，这些属性会被标准的VM（虚拟机）忽略，但是可以包含附件信息。</li>
<li>方法代码表包含一系列对java虚拟机的指令。有些指令在代码中使用偏移量，当指令从方法代码被插入或者移除时，全部偏移量的值可能需要调整。</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="基于事件字节码处理">基于事件字节码处理<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E5%9F%BA%E4%BA%8E%E4%BA%8B%E4%BB%B6%E5%AD%97%E8%8A%82%E7%A0%81%E5%A4%84%E7%90%86" class="hash-link" aria-label="基于事件字节码处理的直接链接" title="基于事件字节码处理的直接链接">​</a></h5>
<p>在Core包中逻辑上分为2部分：</p>
<ul>
<li>字节码生产者，例如ClassReader</li>
<li>字节码消费者，例如writers（ClassWriter, FieldWriter, MethodWriter和AnnotationWriter），adapters（ClassAdapter和MethodAdapter）</li>
</ul>
<p>下图是生产者和消费者交互的时序图：</p>
<p>.png)</p>
<p>通过时序图可以看出ASM在处理class文件的整个过程。ASM通过树这种数据结构来表示复杂的字节码结构，并利用Push模型来对树进行遍历。</p>
<ul>
<li>ASM中提供一个<code>ClassReader</code>类，这个类可以直接由字节数组或者class文件间接的获得字节码数据。它会调用<code>accept</code>方法，接受一个实现了抽象类<code>ClassVisitor</code>的对象实例作为参数，然后依次调用<code>ClassVisitor</code>的各个方法。字节码空间上的偏移被转成各种visitXXX方法。使用者只需要在对应的的方法上进行需求操作即可，无需考虑字节偏移。</li>
<li>这个过程中<code>ClassReader</code>可以看作是一个事件<strong>生产者</strong>，ClassWriter继承自ClassVisitor抽象类，负责将对象化的class文件内容重构成一个二进制格式的class字节码文件，<code>ClassWriter</code>可以看作是一个事件的<strong>消费者</strong>。</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="原java类型与class文件内部类型对应关系">原java类型与class文件内部类型对应关系<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E5%8E%9Fjava%E7%B1%BB%E5%9E%8B%E4%B8%8Eclass%E6%96%87%E4%BB%B6%E5%86%85%E9%83%A8%E7%B1%BB%E5%9E%8B%E5%AF%B9%E5%BA%94%E5%85%B3%E7%B3%BB" class="hash-link" aria-label="原java类型与class文件内部类型对应关系的直接链接" title="原java类型与class文件内部类型对应关系的直接链接">​</a></h5>
<table><thead><tr><th style="text-align:left">Java type</th><th style="text-align:right">Type descriptor</th></tr></thead><tbody><tr><td style="text-align:left">boolean</td><td style="text-align:right">Z</td></tr><tr><td style="text-align:left">char</td><td style="text-align:right">C</td></tr><tr><td style="text-align:left">byte</td><td style="text-align:right">B</td></tr><tr><td style="text-align:left">short</td><td style="text-align:right">S</td></tr><tr><td style="text-align:left">int</td><td style="text-align:right">I</td></tr><tr><td style="text-align:left">float</td><td style="text-align:right">F</td></tr><tr><td style="text-align:left">long</td><td style="text-align:right">J</td></tr><tr><td style="text-align:left">double</td><td style="text-align:right">D</td></tr><tr><td style="text-align:left">Object</td><td style="text-align:right">Ljava/lang/Object;</td></tr><tr><td style="text-align:left">int[]</td><td style="text-align:right">[I</td></tr><tr><td style="text-align:left">Object[][]</td><td style="text-align:right">[[Ljava/lang/Object;</td></tr></tbody></table>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="原java方法声明与class文件内部声明的对应关系">原java方法声明与class文件内部声明的对应关系<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E5%8E%9Fjava%E6%96%B9%E6%B3%95%E5%A3%B0%E6%98%8E%E4%B8%8Eclass%E6%96%87%E4%BB%B6%E5%86%85%E9%83%A8%E5%A3%B0%E6%98%8E%E7%9A%84%E5%AF%B9%E5%BA%94%E5%85%B3%E7%B3%BB" class="hash-link" aria-label="原java方法声明与class文件内部声明的对应关系的直接链接" title="原java方法声明与class文件内部声明的对应关系的直接链接">​</a></h5>
<table><thead><tr><th style="text-align:left">Method declaration in source file</th><th style="text-align:right">Method descriptor</th></tr></thead><tbody><tr><td style="text-align:left">void method(String str,int i,float f)</td><td style="text-align:right">(Ljava/lang/String;IF)V</td></tr><tr><td style="text-align:left">Object method(byte [] b)</td><td style="text-align:right">([B)Ljava/lang/Object;</td></tr><tr><td style="text-align:left">int[] method(double d)</td><td style="text-align:right">(D)[I</td></tr></tbody></table>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="遍历class字节码类信息">遍历CLASS字节码类信息<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E9%81%8D%E5%8E%86class%E5%AD%97%E8%8A%82%E7%A0%81%E7%B1%BB%E4%BF%A1%E6%81%AF" class="hash-link" aria-label="遍历CLASS字节码类信息的直接链接" title="遍历CLASS字节码类信息的直接链接">​</a></h5>
<p>以java.lang.Runnable作为例子</p>
<p>.png)</p>
<p>输出：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">superName=java/lang/Object,name=java/lang/Runnable</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">run()V</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre></div></div>
<p>ClassReader类的accept方法中，有个int类型的flag参数有以下几种：</p>
<ul>
<li><strong>SKIP_DEBUG</strong> 用于忽略debug信息，例如，源文件，行数和变量信息。</li>
<li><strong>SKIP_FRAMES</strong> 用于忽略StackMapTable（栈图）信息。Java 6 之后JVM引入栈图概念。</li>
<li><strong>EXPAND_FRAMES</strong> 扩展StackMapTable数据，允许访问者获取全部本地变量类型与当前堆栈位置的信息。</li>
<li><strong>SKIP_CODE</strong> 排除代码访问的所有方法，同时还通过方法参数属性和注释。</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="通过asm生产自定义类对应的class">通过ASM生产自定义类对应的class<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E9%80%9A%E8%BF%87asm%E7%94%9F%E4%BA%A7%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%AF%B9%E5%BA%94%E7%9A%84class" class="hash-link" aria-label="通过ASM生产自定义类对应的class的直接链接" title="通过ASM生产自定义类对应的class的直接链接">​</a></h5>
<p>目标class内容：</p>
<p>.png)</p>
<p>生产目标class的代码：</p>
<p><em>这里需要注意，平时我们写类的时候，默认的构造方法是可以不写的，但使用ASM框架生产class的话，默认的构造方法是需要写的，不然，无法实例化对象。</em></p>
<p>创建类、构造函数与字段：</p>
<p>.png)</p>
<p>创建showInfo方法</p>
<p>.png)</p>
<p>创建get、set方法</p>
<p>.png)</p>
<p>最后生产出Person.class之后，我们可以使用JD-GUI打开：</p>
<p>.png)</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="动态加载生产出的class字节码并实例化该类">动态加载生产出的class字节码并实例化该类<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E7%94%9F%E4%BA%A7%E5%87%BA%E7%9A%84class%E5%AD%97%E8%8A%82%E7%A0%81%E5%B9%B6%E5%AE%9E%E4%BE%8B%E5%8C%96%E8%AF%A5%E7%B1%BB" class="hash-link" aria-label="动态加载生产出的class字节码并实例化该类的直接链接" title="动态加载生产出的class字节码并实例化该类的直接链接">​</a></h5>
<p>我们可以通过<code>ClassWriter</code>中的<code>toByteArray()</code> 方法可以获取生成的字节码数据。然后使用<code>ClassLoader</code>的<code>defineClass()</code>方法进行反射实例化对象，并调用<code>showInfo()</code>方法。</p>
<p>0.png)</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="动态修改class字节码进行aop编程">动态修改class字节码，进行AOP编程<a href="https://wawov.com/blog/2016/06/22/asm-bytecode-framework#%E5%8A%A8%E6%80%81%E4%BF%AE%E6%94%B9class%E5%AD%97%E8%8A%82%E7%A0%81%E8%BF%9B%E8%A1%8Caop%E7%BC%96%E7%A8%8B" class="hash-link" aria-label="动态修改class字节码，进行AOP编程的直接链接" title="动态修改class字节码，进行AOP编程的直接链接">​</a></h5>
<p>通过加载上面生成的<code>Person.class</code>文件，在<code>showInfo()</code>方法里面添加一行打印当前时间。</p>
<p>通过继承ClassVisitor，重写<code>visitMethod()</code>，拦截<code>showInfo()</code>方法。</p>
<p>1.png)</p>
<p>然后让继承<code>AdviceAdapter</code>的类中的<code>onMethodEnter()</code>方法修改<code>showInfo()</code>方法。</p>
<p>2.png)</p>
<p>这样就可以实现修改class字节码的操作了。重新生成class文件。使用JD－GUI验证一下。不出意料，结果是我们所预期的。</p>
<p>3.png)</p>
<p>虽然例子简单，但是是进行AOP"无损注入"的基础展示。著名的Spring框架也是利用这种技术实现AOP的。至此，对ASM框架的一些简单的使用就是这样了，其中会涉及到一些JVM操作的理解，可以查看我的另一篇文章：<a href="http://www.jianshu.com/p/9f09a0c21542" target="_blank" rel="noopener noreferrer">JVM指令</a></p>
<p>另外，可以到github仓库查看本次的demo工程：<a href="https://github.com/ledboot/ASMTest" target="_blank" rel="noopener noreferrer">ASMTest</a></p>]]></content:encoded>
            <category>java</category>
            <category>ASM</category>
        </item>
        <item>
            <title><![CDATA[JVM指令]]></title>
            <link>https://wawov.com/blog/2016/06/22/jvm-instructions</link>
            <guid>https://wawov.com/blog/2016/06/22/jvm-instructions</guid>
            <pubDate>Wed, 22 Jun 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[凡是带const的表示将什么数据压操作数栈]]></description>
            <content:encoded><![CDATA[<h5 class="anchor anchorWithStickyNavbar_LWe7" id="凡是带const的表示将什么数据压操作数栈">凡是带<strong>const</strong>的表示将什么数据压操作数栈<a href="https://wawov.com/blog/2016/06/22/jvm-instructions#%E5%87%A1%E6%98%AF%E5%B8%A6const%E7%9A%84%E8%A1%A8%E7%A4%BA%E5%B0%86%E4%BB%80%E4%B9%88%E6%95%B0%E6%8D%AE%E5%8E%8B%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88" class="hash-link" aria-label="凡是带const的表示将什么数据压操作数栈的直接链接" title="凡是带const的表示将什么数据压操作数栈的直接链接">​</a></h5>
<ul>
<li><code>iconst_2</code> 将int型数据2压入到操作数栈；</li>
<li><code>aconst_null</code>  将null值压入栈；</li>
<li><code>bipush</code>和<code>sipush</code>  表示将单字节或者短整形的常量值压入操作数栈；</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="带ldc的表示将什么类型数据从常量池中压入到操作数栈">带ldc的表示将什么类型数据从常量池中压入到操作数栈。<a href="https://wawov.com/blog/2016/06/22/jvm-instructions#%E5%B8%A6ldc%E7%9A%84%E8%A1%A8%E7%A4%BA%E5%B0%86%E4%BB%80%E4%B9%88%E7%B1%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E4%BB%8E%E5%B8%B8%E9%87%8F%E6%B1%A0%E4%B8%AD%E5%8E%8B%E5%85%A5%E5%88%B0%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88" class="hash-link" aria-label="带ldc的表示将什么类型数据从常量池中压入到操作数栈。的直接链接" title="带ldc的表示将什么类型数据从常量池中压入到操作数栈。的直接链接">​</a></h5>
<ul>
<li><code>ldc_w</code>  将int或者flat或者string类型的数据压入到操作数栈；</li>
<li><code>ldc2_w</code>  将long或者double类型的数据压入到操作数栈；</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="凡是带load的指令表示将某类型的局部变量数据压入到操作数栈的栈顶">凡是带load的指令表示将某类型的局部变量数据压入到操作数栈的栈顶。<a href="https://wawov.com/blog/2016/06/22/jvm-instructions#%E5%87%A1%E6%98%AF%E5%B8%A6load%E7%9A%84%E6%8C%87%E4%BB%A4%E8%A1%A8%E7%A4%BA%E5%B0%86%E6%9F%90%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E6%95%B0%E6%8D%AE%E5%8E%8B%E5%85%A5%E5%88%B0%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88%E7%9A%84%E6%A0%88%E9%A1%B6" class="hash-link" aria-label="凡是带load的指令表示将某类型的局部变量数据压入到操作数栈的栈顶。的直接链接" title="凡是带load的指令表示将某类型的局部变量数据压入到操作数栈的栈顶。的直接链接">​</a></h5>
<ul>
<li><code>iload</code> 表示将int类型的局部变量压入到操作数栈的栈顶；</li>
<li><code>aload</code>  以a开头的表示将引用类型的局部变量压入到操作数栈的栈顶；</li>
<li><code>iload_1</code> 将局部变量数组里面下标为1的int类型的数据压入到操作数栈；</li>
<li><code>iaload</code>   将int型数组的指定索引的值压入到操作数栈；</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="凡是带有store指令的表示将操作数栈顶的某类型的值存入指定的局部变量中">凡是带有store指令的表示将操作数栈顶的某类型的值存入指定的局部变量中。<a href="https://wawov.com/blog/2016/06/22/jvm-instructions#%E5%87%A1%E6%98%AF%E5%B8%A6%E6%9C%89store%E6%8C%87%E4%BB%A4%E7%9A%84%E8%A1%A8%E7%A4%BA%E5%B0%86%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88%E9%A1%B6%E7%9A%84%E6%9F%90%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%80%BC%E5%AD%98%E5%85%A5%E6%8C%87%E5%AE%9A%E7%9A%84%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E4%B8%AD" class="hash-link" aria-label="凡是带有store指令的表示将操作数栈顶的某类型的值存入指定的局部变量中。的直接链接" title="凡是带有store指令的表示将操作数栈顶的某类型的值存入指定的局部变量中。的直接链接">​</a></h5>
<ul>
<li><code>istore</code>  表示将栈顶int类型的数据存入到指定的局部变量中；</li>
<li><code>istore_3</code>  表示将栈int类型的数据存入到局部变量数组的下标为3的元素中；</li>
<li><code>pop</code>  将栈顶数据弹出；</li>
<li><code>pop2</code>将栈顶的一个long或者double数据从栈顶弹出来；</li>
<li><code>dup</code>  复制栈顶的数据并将复制的值也压入到栈顶；</li>
<li><code>dup2</code>  复制栈顶一个long或者是double的数据并将复制的值也压入到栈顶；</li>
<li><code>swap</code>  将栈最顶端的两个值互换；</li>
<li><code>iadd</code> 将栈顶两个int型的数据相加然后将结果再次的压入到栈顶；</li>
<li><code>isub</code> 将栈顶两个int型的数据相减然后将结果再次的压入到栈顶；</li>
<li><code>imul</code> 将栈顶两个int型的数据相乘然后将结果再次的压入到栈顶；</li>
<li><code>idiv</code>  将栈顶两个int型的数据相除然后将结果再次的压入到栈顶；</li>
<li><code>irem</code> 将栈顶两个int型的数据取模运算然后将结果再次的压入到栈顶；</li>
<li><code>ineg</code> 将栈顶的int数据取负将结果压入到栈顶；</li>
<li><code>iinc</code>  将指定的int变量增加指定值(i++,i--,i+=2)；</li>
<li><code>i2l</code>   将栈顶int类型数据强制转换成long型将结果压入到栈顶；</li>
<li><code>lcmp</code>  将栈顶两long型数据的大小进行比较，并将结果(1,0,-1)压入栈顶;</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="以if开头的指令都是跳转指令">以if开头的指令都是跳转指令<a href="https://wawov.com/blog/2016/06/22/jvm-instructions#%E4%BB%A5if%E5%BC%80%E5%A4%B4%E7%9A%84%E6%8C%87%E4%BB%A4%E9%83%BD%E6%98%AF%E8%B7%B3%E8%BD%AC%E6%8C%87%E4%BB%A4" class="hash-link" aria-label="以if开头的指令都是跳转指令的直接链接" title="以if开头的指令都是跳转指令的直接链接">​</a></h5>
<ul>
<li><code>tableswitch</code>、<code>lookupswitch</code>  表示用switch条件跳转；</li>
<li><code>ireturn</code>  从当前方法返回int型数据；</li>
<li><code>getstatic</code>  获取指定类的静态域，将将结果压入到栈顶；</li>
<li><code>putstatic</code> 为指定的类的静态域赋值；</li>
<li><code>getfield</code>   获取指定类的实例变量，将结果压入到栈顶；</li>
<li><code>putfield</code>   为指定类的实例变量赋值；</li>
<li><code>invokevirtual</code>  调用实例方法；</li>
<li><code>invokespacial</code>  调用超类构造方法，实例初始化方法，私有方法；</li>
<li><code>invokestatic</code>  调用静态方法；</li>
<li><code>invokeinterface</code>  调用接口方法；</li>
<li><code>new</code> 创建一个对象，并将其引用压入到栈顶；</li>
<li><code>newarray</code>  创建一个原始类型的数组，并将其引用压入到栈顶；</li>
<li><code>arraylength</code>   获得一个数组的长度，将将结果压入到栈顶；</li>
<li><code>athrow</code>   将栈顶的异常抛出；</li>
<li><code>checkcast</code>  检验类型转换，转换未通过，将抛出ClassCastException；</li>
<li><code>instanceof</code> 检验对象是否是指定的类的实例，如果是将1压入栈顶，否则将0压入栈顶</li>
<li><code>monitorenter</code>   获得对象的锁，用于同步方法或同步块</li>
<li><code>monitorexit</code>    释放对象的锁，用于同步方法或同步块</li>
<li><code>ifnull</code>    为null时跳转</li>
<li><code>ifnonnull</code>   不为null时跳转</li>
</ul>]]></content:encoded>
            <category>JVM</category>
        </item>
        <item>
            <title><![CDATA[Android webview交互性能监测指标获取方法]]></title>
            <link>https://wawov.com/blog/2016/06/22/webview-monitoring</link>
            <guid>https://wawov.com/blog/2016/06/22/webview-monitoring</guid>
            <pubDate>Wed, 22 Jun 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[业界衡量移动web app交互性能的优劣主要是通过监测webview渲染页面时白屏时间，DOM树构建时间，整页时间和首屏时间这三个指标来完成的，那么这四个指标分别的意义是什么呢？我们从w3c提供的navigation Timing中看到交互性能指的是Processing和onLoad这两部分的时间。]]></description>
            <content:encoded><![CDATA[<p>业界衡量移动web app交互性能的优劣主要是通过监测webview渲染页面时白屏时间，DOM树构建时间，整页时间和首屏时间这三个指标来完成的，那么这四个指标分别的意义是什么呢？我们从w3c提供的navigation Timing中看到交互性能指的是Processing和onLoad这两部分的时间。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset//img/2024/202409101704925.png" alt="timing-overview.png" class="img_ev3q"></p>
<p>在浏览器交互阶段（Processing和onLoad时间段）浏览器接收服务器返回的基础页数据后，浏览器需要对HTML这个单纯的文本内容进行解析，从文本中构建出一个内部数据结构，叫做DOM树（DOM tree），用于组织将要绘制在屏幕上的内容。从HTML也能得到外联或内联的CSS脚本和JavaScript脚本，当然还有媒体文件，比如图片、视频、声音，这些都需要再次发起网络请求下载。CSS文本内容中的规则同样会被构建成一个内部数据结构，叫做CSS树（CSS tree），来决定DOM树的节点在屏幕上的布局、颜色、状态效果。JavaScript脚本被触发执行后，除了计算业务，往往还需要操作DOM树，就是所谓的DOM API。</p>
<p><img decoding="async" loading="lazy" src="https://cdn.wawov.com/gh/ledboot/blog-asset//img/2024/202409101705696.png" alt="webkitflow.png" class="img_ev3q"></p>
<ul>
<li>
<p>白屏时间
***指浏览器开始显示内容的时间。***但是在传统的采集方式里，是在HTML的head标签结尾里记录时间戳，来计算白屏时间。在这个时刻，浏览器开始解析body标签内的内容。而现代浏览器不会等待CSS树（所有CSS文件下载和解析完成）和DOM树（整个body标签解析完成）构建完成才开始绘制，而是马上开始显示中间结果。所以经常在低网速的环境中，观察到页面由上至下缓慢显示完，或者先显示文本内容后再重绘成带有格式的页面内容。在android中我们通过使用<code>webview.WebChromeClient</code>的<code>onReceivedTitle</code>事件来近似获得白屏时间。</p>
</li>
<li>
<p>DOM树构建时间
<em><strong>指浏览器开始对基础页文本内容进行解析到从文本中构建出一个内部数据结构（DOM树）的时间</strong></em>，这个事件是从HTML中的onLoad的延伸而来的，当一个页面完成加载时，初始化脚本的方法是使用load事件，但这个类函数的缺点是仅在所有资源都完全加载后才被触发，这有时会导致比较严重的延迟，开发人员随后创建了domready事件，它在DOM加载之后及资源加载之前被触发。domready被众多JavaScript库所采用,它在本地浏览器中以<code>DOMContentLoaded</code>事件的形式被使用。在android中我们通过注入js代码到webview中的方式来实现；具体实现上，在<code>WebChromeClient</code>的<code>onReceivedTitle</code>事件被触发时注入我们的js代码，然后通过<code>WebChromeClient</code>的<code>onJsPrompt</code>事件来获取domc（<code>window.DOMContentLoaded</code>事件）时间。</p>
</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void onReceivedTitle (WebView view, String title) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    view.loadUrl("javascript:" + </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	"window.addEventListener('DOMContentLoaded', function() {" +</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "prompt('domc:' + new Date().getTime());" + </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    );</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(message != null){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(!preCacheRun){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			String[] strs = message.split(":");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if(2 == strs.length){	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				if("domc".equals(strs[0])){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					result.getCurrentRun().setDocComplete(Long.valueOf(strs[1].trim()));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	r.confirm(defaultValue);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<ul>
<li>首屏时间
指从网页应用的角度定义的指标，在Navigation Timing或者浏览器实现中并没有相关指标值。首屏时间，***是指用户看到第一屏，即整个网页顶部大小为当前窗口的区域，显示完整的时间。***常用的方法有，页面标签标记法、图像相似度比较法和首屏高度内图片加载法；</li>
</ul>
<ol>
<li>页面标签标记法，在HTML文档中对应首屏内容的标签结束位置，使用内联的JavaScript代码记录当前时间戳，比较局限；</li>
<li>图像相似度比较法，通过比较连续截屏图像的像素点变化趋势确定首屏时间，最为科学和直观的方式，但是比较消耗本地设备的运行资源；</li>
<li>首屏高度内图片加载法，通过寻找首屏区域内的所有图片，计算它们加载完的时间去得到首屏时间，这样比较符合网页的实际体验并且比较节省设备运行资源；
具体实现上我采用的是最后一种，即"首屏高度内图片加载法"；因为通常需要考虑首屏时间的页面，都是因为在首屏位置内放入了较多的图片资源。现代浏览器处理图片资源时是异步的，会先将图片长宽应用于页面排版，然后随着收到图片数据由上至下绘制显示的。并且浏览器对每个页面的TCP连接数限制，使得并不是所有图片都能立刻开始下载和显示。因此我们在DOM树构建完成后即可遍历获得所有在设备屏幕高度内的所有图片资源标签，在所有图片标签中添加document.onload事件，在整页加载完成（window.onLoad事件发生）时遍历图片标签并获得之前注册的document.onload事件时间的最大值，该最大值减去navigationStart即认为近似的首屏时间。在android中我们通过注入js代码到webview中的方式来实现；具体实现上，在WebChromeClient的onReceivedTitle事件被触发时注入我们的js代码，然后通过<code>WebChromeClient</code>的<code>onJsPrompt</code>事件来获取firstscreen时间。</li>
</ol>
<p>js部分计算首屏时间的逻辑代码：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">function first_screen () {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	var imgs = document.getElementsByTagName("img"), fs = +new Date;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	var fsItems = [], that = this;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    function getOffsetTop(elem) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var top = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        top = window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        try{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            top += elem.getBoundingClientRect().top;    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }catch(e){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }finally{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            return top;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var loadEvent = function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        //gif避免</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if (this.removeEventListener) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            this.removeEventListener("load", loadEvent, false);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        fsItems.push({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            img : this,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            time : +new Date</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for (var i = 0; i &lt; imgs.length; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        (function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            var img = imgs[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            if (img.addEventListener) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                !img.complete &amp;&amp; img.addEventListener("load", loadEvent, false);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            } else if (img.attachEvent) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                img.attachEvent("onreadystatechange", function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    if (img.readyState == "complete") {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        loadEvent.call(img, loadEvent);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        })();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    function firstscreen_time() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var sh = document.documentElement.clientHeight;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        for (var i = 0; i &lt; fsItems.length; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            var item = fsItems[i], img = item['img'], time = item['time'], top = getOffsetTop(img);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            if (top &gt; 0 &amp;&amp; top &lt; sh) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                fs = time &gt; fs ? time : fs;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return fs;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    window.addEventListener('load', function() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    						prompt('firstscreen:' + firstscreen_time());</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    					});</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>webview的注入代码：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">private void registerOnLoadHandler(WebView view) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    String jscontent = getJavaScriptAsString();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    view.loadUrl("javascript:" + jscontent + </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      "window.addEventListener('DOMContentLoaded', function() {" +</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          "first_screen();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       });"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    );</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void onReceivedTitle (WebView view, String title) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	registerOnLoadHandler(view);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(message != null){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(!preCacheRun){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			String[] strs = message.split(":");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if(2 == strs.length){	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				if("firstscreen".equals(strs[0])){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					result.getCurrentRun().setFirstScreen(Long.valueOf(strs[1].trim()));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	r.confirm(defaultValue);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<ul>
<li>整页时间
***指页面完成整个加载过程的时刻。***从Navigation Timing API上采集，就是loadEventEnd减去navigationStart。在传统采集方法中，会使用window对象的onload事件来记录时间戳，它表示浏览器认定该页面已经载入完全了。android中我们通过注入js代码到webview中的方式来实现；具体实现上，在WebChromeClient的onReceivedTitle事件被触发时注入我们的js代码，然后通过<code>WebChromeClient</code>的<code>onJsPrompt</code>事件来获取load（window.onLoad事件）时间。</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void onReceivedTitle (WebView view, String title) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    view.loadUrl("javascript:" + </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	"window.addEventListener('load', function() {" +</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "prompt('load:' + new Date().getTime());" + </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    );</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@Override</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(message != null){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(!preCacheRun){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			String[] strs = message.split(":");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if(2 == strs.length){	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				if("load".equals(strs[0])){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					result.getCurrentRun().setFullyLoaded(Long.valueOf(strs[1].trim()));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	r.confirm(defaultValue);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>Webview</category>
        </item>
        <item>
            <title><![CDATA[java 数组]]></title>
            <link>https://wawov.com/blog/2016/05/09/java-array</link>
            <guid>https://wawov.com/blog/2016/05/09/java-array</guid>
            <pubDate>Mon, 09 May 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[一维数组]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="一维数组">一维数组<a href="https://wawov.com/blog/2016/05/09/java-array#%E4%B8%80%E7%BB%B4%E6%95%B0%E7%BB%84" class="hash-link" aria-label="一维数组的直接链接" title="一维数组的直接链接">​</a></h4>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="数组初始化">数组初始化<a href="https://wawov.com/blog/2016/05/09/java-array#%E6%95%B0%E7%BB%84%E5%88%9D%E5%A7%8B%E5%8C%96" class="hash-link" aria-label="数组初始化的直接链接" title="数组初始化的直接链接">​</a></h5>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//静态初始化</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[] a = {1,2,3};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[] a = new int[]{1,2,3};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//动态初始化</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[] a = new int[3];</span><br></span></code></pre></div></div>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="数组遍历">数组遍历<a href="https://wawov.com/blog/2016/05/09/java-array#%E6%95%B0%E7%BB%84%E9%81%8D%E5%8E%86" class="hash-link" aria-label="数组遍历的直接链接" title="数组遍历的直接链接">​</a></h5>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//普通for循环</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">for(int i=0;i&lt;a.length;i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	System.out.println(a[i]);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//增强for循环</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">for(int i:a){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	System.out.println(i);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="二维数组">二维数组<a href="https://wawov.com/blog/2016/05/09/java-array#%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84" class="hash-link" aria-label="二维数组的直接链接" title="二维数组的直接链接">​</a></h4>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="初始化">初始化<a href="https://wawov.com/blog/2016/05/09/java-array#%E5%88%9D%E5%A7%8B%E5%8C%96" class="hash-link" aria-label="初始化的直接链接" title="初始化的直接链接">​</a></h5>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//静态初始化</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[][] a = {{1,2},{3,4}};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//动态初始化</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[][] a = new int[3][3];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int[][] a = new int[3][];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">a[0] = new int[3];</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="数组复制">数组复制<a href="https://wawov.com/blog/2016/05/09/java-array#%E6%95%B0%E7%BB%84%E5%A4%8D%E5%88%B6" class="hash-link" aria-label="数组复制的直接链接" title="数组复制的直接链接">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">System.arraycopy(src, srcPos, dest, destPos, length);</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="数组排序">数组排序<a href="https://wawov.com/blog/2016/05/09/java-array#%E6%95%B0%E7%BB%84%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="数组排序的直接链接" title="数组排序的直接链接">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Arrays.sort(a);</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="数组查找">数组查找<a href="https://wawov.com/blog/2016/05/09/java-array#%E6%95%B0%E7%BB%84%E6%9F%A5%E6%89%BE" class="hash-link" aria-label="数组查找的直接链接" title="数组查找的直接链接">​</a></h4>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//二分查找（前提是已排序）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Arrays.binarySearch(a, key);</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>java</category>
            <category>java基础</category>
        </item>
        <item>
            <title><![CDATA[java集合]]></title>
            <link>https://wawov.com/blog/2016/05/09/java-collection</link>
            <guid>https://wawov.com/blog/2016/05/09/java-collection</guid>
            <pubDate>Mon, 09 May 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[- 所有实现Collection接口的类都必须提供两个标准的构造函数：无参数的构造函数用于创建一个空的Collection，有一个Collection参数的构造函数用于创建一个新的Collection，这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。]]></description>
            <content:encoded><![CDATA[<ul>
<li>
<p>所有实现Collection接口的类都必须提供两个标准的构造函数：无参数的构造函数用于创建一个空的Collection，有一个Collection参数的构造函数用于创建一个新的Collection，这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。</p>
</li>
<li>
<p>Collection接口派生的两个常用接口是List和Set。</p>
</li>
<li>
<p>List是有序的Collection，使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引（元素在List中的位置，类似于数组下标）来访问List中的元素，这类似于Java的数组，List允许有相同的元素。</p>
</li>
<li>
<p>实现List接口的常用类有LinkedList，ArrayList，Vector和Stack</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="list接口">List接口<a href="https://wawov.com/blog/2016/05/09/java-collection#list%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="List接口的直接链接" title="List接口的直接链接">​</a></h3>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="linkedlist类">LinkedList类<a href="https://wawov.com/blog/2016/05/09/java-collection#linkedlist%E7%B1%BB" class="hash-link" aria-label="LinkedList类的直接链接" title="LinkedList类的直接链接">​</a></h5>
<p>LinkedList实现了List接口，<strong>允许null元素</strong>。此外LinkedList提供额外的get，remove，insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈（stack），队列（queue）或双向队列（deque）。
注意LinkedList没有同步方法。如果多个线程同时访问一个List，则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List：
List list = Collections.synchronizedList(new LinkedList(...));</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="arraylist类">ArrayList类<a href="https://wawov.com/blog/2016/05/09/java-collection#arraylist%E7%B1%BB" class="hash-link" aria-label="ArrayList类的直接链接" title="ArrayList类的直接链接">​</a></h5>
<p>ArrayList实现了可变大小的数组。它允许所有元素，包括null。ArrayList没有同步。
size，isEmpty，get，set方法运行时间为常数。但是add方法开销为分摊的常数，添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量（Capacity），即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加，但是增长算法并没有定义。当需要插入大量元素时，在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样，<strong>ArrayList也是非同步的</strong>（unsynchronized）。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="vector类">Vector类<a href="https://wawov.com/blog/2016/05/09/java-collection#vector%E7%B1%BB" class="hash-link" aria-label="Vector类的直接链接" title="Vector类的直接链接">​</a></h5>
<p>Vector非常类似ArrayList，但是<strong>Vector是同步的</strong>。由Vector创建的Iterator，虽然和ArrayList创建的Iterator是同一接口，但是，因为Vector是同步的，当一个Iterator被创建而且正在被使用，另一个线程改变了Vector的状态（例如，添加或删除了一些元素），这时调用Iterator的方法时将抛出ConcurrentModificationException，因此必须捕获该异常。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="stack-类">Stack 类<a href="https://wawov.com/blog/2016/05/09/java-collection#stack-%E7%B1%BB" class="hash-link" aria-label="Stack 类的直接链接" title="Stack 类的直接链接">​</a></h5>
<p>Stack继承自Vector，实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法，还有peek方法得到栈顶的元素，empty方法测试堆栈是否为空，search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="set接口">Set接口<a href="https://wawov.com/blog/2016/05/09/java-collection#set%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="Set接口的直接链接" title="Set接口的直接链接">​</a></h3>
<p>Set是一种<strong>不包含重复</strong>的元素的Collection，即任意的两个元素e1和e2都有e1.equals(e2)=false，Set最多有一个null元素。很明显，Set的构造函数有一个约束条件，传入的Collection参数不能包含重复的元素。
请注意：必须小心操作可变对象（Mutable Object）。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="map接口">Map接口<a href="https://wawov.com/blog/2016/05/09/java-collection#map%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="Map接口的直接链接" title="Map接口的直接链接">​</a></h3>
<p>请注意，Map没有继承Collection接口，Map提供key到value的映射。一个Map中不能包含相同的key，每个key只能映射一个value。Map接口提供3种集合的视图，Map的内容可以被当作一组key集合，一组value集合，或者一组key-value映射。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="hashtable类">Hashtable类<a href="https://wawov.com/blog/2016/05/09/java-collection#hashtable%E7%B1%BB" class="hash-link" aria-label="Hashtable类的直接链接" title="Hashtable类的直接链接">​</a></h5>
<p>Hashtable继承Map接口，<strong>Hashtable是同步的</strong>，实现一个key-value映射的哈希表。任何非空（non-null）的对象都可作为key或者value。</p>
<p>作为key的对象将通过计算其散列函数来确定与之对应的value的位置，因此任何作为key的对象都必须实现hashCode和equals方法。</p>
<p>如果相同的对象有不同的hashCode，对哈希表的操作会出现意想不到的结果（期待的get方法返回null），要避免这种问题，只需要牢记一条：要同时复写equals方法和hashCode方法，而不要只写其中一个。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="hashmap类">HashMap类<a href="https://wawov.com/blog/2016/05/09/java-collection#hashmap%E7%B1%BB" class="hash-link" aria-label="HashMap类的直接链接" title="HashMap类的直接链接">​</a></h5>
<p>HashMap和Hashtable类似，不同之处在于<strong>HashMap是非同步的</strong>，并且允许null，即null value和null key。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="weakhashmap类">WeakHashMap类<a href="https://wawov.com/blog/2016/05/09/java-collection#weakhashmap%E7%B1%BB" class="hash-link" aria-label="WeakHashMap类的直接链接" title="WeakHashMap类的直接链接">​</a></h5>
<p>WeakHashMap是一种改进的HashMap，它对key实行"弱引用"，如果一个key不再被外部所引用，那么该key可以被GC回收。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a href="https://wawov.com/blog/2016/05/09/java-collection#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h3>
<ul>
<li>如果涉及到堆栈，队列等操作，应该考虑用List，对于需要快速插入，删除元素，应该使用LinkedList，如果需要快速随机访问元素，应该使用ArrayList。</li>
<li>如果程序在单线程环境中，或者访问仅仅在一个线程中进行，考虑非同步的类，其效率较高，如果多个线程可能同时操作一个类，应该使用同步的类。</li>
<li>要特别注意对哈希表的操作，作为key的对象要正确复写equals和hashCode方法。</li>
<li>尽量返回接口而非实际的类型，如返回List而非ArrayList，这样如果以后需要将ArrayList换成LinkedList时，客户端代码不用改变。这就是针对抽象编程.</li>
</ul>]]></content:encoded>
            <category>java</category>
            <category>java基础</category>
        </item>
        <item>
            <title><![CDATA[java 面向对象——抽象类与接口]]></title>
            <link>https://wawov.com/blog/2016/05/09/java-object-oriented-abstract-interface</link>
            <guid>https://wawov.com/blog/2016/05/09/java-object-oriented-abstract-interface</guid>
            <pubDate>Mon, 09 May 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[抽象类]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="抽象类">抽象类<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-abstract-interface#%E6%8A%BD%E8%B1%A1%E7%B1%BB" class="hash-link" aria-label="抽象类的直接链接" title="抽象类的直接链接">​</a></h4>
<p>抽象类只是比普通类多了一些抽象方法而已，抽象方法没有方法体。</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">abstract class Person{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public abstract void walk();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>注意：</p>
<ul>
<li>抽象类不能直接实例化（new Person()是错误的），只能通过子类实例化</li>
<li>抽象类也包含构造方法（子类调用父类构造方法）</li>
<li>抽象类也可以包含普通方法</li>
</ul>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="接口">接口<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-abstract-interface#%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="接口的直接链接" title="接口的直接链接">​</a></h4>
<p>接口是抽象类的更一步抽象，接口中所有的方法都是抽象方法</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">interface Person{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public void walk();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public void eat();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>注意：</p>
<ul>
<li>接口不能被实例化</li>
<li>接口没有构造方法</li>
<li>接口中的方法必须是抽象方法（jdk1.8后可以有default方法）</li>
<li>接口中的常量必须是public static final</li>
</ul>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="抽象类与接口的区别">抽象类与接口的区别<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-abstract-interface#%E6%8A%BD%E8%B1%A1%E7%B1%BB%E4%B8%8E%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%8C%BA%E5%88%AB" class="hash-link" aria-label="抽象类与接口的区别的直接链接" title="抽象类与接口的区别的直接链接">​</a></h4>
<table><thead><tr><th></th><th>抽象类</th><th>接口</th></tr></thead><tbody><tr><td>成员变量</td><td>无特殊要求</td><td>必须是public static final</td></tr><tr><td>构造方法</td><td>有</td><td>无</td></tr><tr><td>方法</td><td>可以有抽象方法也可以有普通方法</td><td>只能是抽象方法（jdk1.8后可以有default）</td></tr><tr><td>继承</td><td>只能单继承</td><td>可以多实现</td></tr><tr><td>成员</td><td>可以有普通成员</td><td>只能是常量</td></tr></tbody></table>]]></content:encoded>
            <category>java</category>
            <category>java基础</category>
        </item>
        <item>
            <title><![CDATA[java 面向对象——类与对象的概念和使用]]></title>
            <link>https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj</link>
            <guid>https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj</guid>
            <pubDate>Mon, 09 May 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[方法]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="方法">方法<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E6%96%B9%E6%B3%95" class="hash-link" aria-label="方法的直接链接" title="方法的直接链接">​</a></h4>
<p>方法就是一段可重复调用的代码段</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="方法重载">方法重载<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E6%96%B9%E6%B3%95%E9%87%8D%E8%BD%BD" class="hash-link" aria-label="方法重载的直接链接" title="方法重载的直接链接">​</a></h5>
<p>方法的重载：方法名称相同，但是参数的类型和个数不同，通过传递参数的个数和类型不同来完成不同的功能。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="方法重写">方法重写<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99" class="hash-link" aria-label="方法重写的直接链接" title="方法重写的直接链接">​</a></h5>
<p>父类与子类之间的多态性，对父类的函数进行重新定义。如果子类中定义某方法与其父类有相同的名称和参数，我们说这个方法被重写，方法重写又称方法覆盖。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="方法递归">方法递归<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E6%96%B9%E6%B3%95%E9%80%92%E5%BD%92" class="hash-link" aria-label="方法递归的直接链接" title="方法递归的直接链接">​</a></h5>
<p>一种特殊的调用形式，就是方法自己调用自己。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public static int add(int num){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(num == 1){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}else{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return num+add(num-1);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="类">类<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E7%B1%BB" class="hash-link" aria-label="类的直接链接" title="类的直接链接">​</a></h4>
<p>类是对某一类事物的描述，是抽象的、概念上的意义，对象是实际存在的该类事物的每一个个体，也被称为实例。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="内存分配">内存分配<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D" class="hash-link" aria-label="内存分配的直接链接" title="内存分配的直接链接">​</a></h5>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Person persion = new Person();</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="面向对象最重要的特征">面向对象最重要的特征<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%9C%80%E9%87%8D%E8%A6%81%E7%9A%84%E7%89%B9%E5%BE%81" class="hash-link" aria-label="面向对象最重要的特征的直接链接" title="面向对象最重要的特征的直接链接">​</a></h4>
<ul>
<li>封装，对外部不可见</li>
<li>继承，扩展类的功能</li>
<li>多态，方法的重载，对象的多态性,面向对象的精髓所在</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="封装性">封装性<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E5%B0%81%E8%A3%85%E6%80%A7" class="hash-link" aria-label="封装性的直接链接" title="封装性的直接链接">​</a></h5>
<p>使用关键字private修饰属性和方法</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="多态性">多态性<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E5%A4%9A%E6%80%81%E6%80%A7" class="hash-link" aria-label="多态性的直接链接" title="多态性的直接链接">​</a></h5>
<ul>
<li>向上转型，父类对象 ＝ 子类实例</li>
<li>向下转型，子类对象 ＝ （子类）父类实例</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="匿名对象">匿名对象<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E5%8C%BF%E5%90%8D%E5%AF%B9%E8%B1%A1" class="hash-link" aria-label="匿名对象的直接链接" title="匿名对象的直接链接">​</a></h5>
<p>匿名对象就是没有名字的对象，如果程序只是用一次该对象，就可以使用匿名对象的方式。</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="构造方法">构造方法<a href="https://wawov.com/blog/2016/05/09/java-object-oriented-class-obj#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95" class="hash-link" aria-label="构造方法的直接链接" title="构造方法的直接链接">​</a></h5>
<ul>
<li>构造方法名称必须与类名一致</li>
<li>构造方法没有返回值</li>
<li>每个类实例化之后都会调用构造方法，如果没有构造方法，程序在编译的时候会创建一个无参的构造方法</li>
<li>构造方法可以重载</li>
</ul>]]></content:encoded>
            <category>java</category>
            <category>java基础</category>
        </item>
        <item>
            <title><![CDATA[Android基础]]></title>
            <link>https://wawov.com/blog/2015/12/16/android-base</link>
            <guid>https://wawov.com/blog/2015/12/16/android-base</guid>
            <pubDate>Wed, 16 Dec 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[ThreadLocal]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="threadlocal">ThreadLocal<a href="https://wawov.com/blog/2015/12/16/android-base#threadlocal" class="hash-link" aria-label="ThreadLocal的直接链接" title="ThreadLocal的直接链接">​</a></h4>
<p>ThreadLocal是如何做到为每一个线程维护变量的副本的呢？其实实现的思路很简单：在ThreadLocal类中有一个Map，用于存储每一个线程的变量副本，Map中元素的键为线程对象，而值对应线程的变量副本。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="javalangthreadlocalt的具体实现">java.lang.ThreadLocal&lt;T&gt;的具体实现<a href="https://wawov.com/blog/2015/12/16/android-base#javalangthreadlocalt%E7%9A%84%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="java.lang.ThreadLocal<T>的具体实现的直接链接" title="java.lang.ThreadLocal<T>的具体实现的直接链接">​</a></h4>
<p>先来看一下ThreadLocal的set()方法的源码是如何实现的：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     * Sets the current thread's copy of this thread-local variable</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     * to the specified value.  Most subclasses will have no need to</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     * override this method, relying solely on the {@link #initialValue}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     * method to set the values of thread-locals.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     *</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     * @param value the value to be stored in the current thread's copy of</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     *        this thread-local.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     */</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    public void set(T value) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Thread t = Thread.currentThread();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ThreadLocalMap map = getMap(t);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if (map != null)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            map.set(this, value);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            createMap(t, value);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ThreadLocalMap getMap(Thread t) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return t.threadLocals;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span></code></pre></div></div>
<p>通过getMap获取当前线程的ThreadLocalMap，然后将变量设置到这个ThreadLocalMap中，如果ThreadLocalMap为空时，则通过createMap方法创建。</p>
<p>线程隔离的秘密，就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个<strong>静态内部类</strong>，它实现了键值对的设置和获取（对比Map对象来理解），每个线程中都有一个独立的ThreadLocalMap副本，它所存储的值，只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本，从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的，完全不会有并发错误。还有一点就是，ThreadLocalMap存储的键值对中的键是<strong>this对象指向的ThreadLocal对象</strong>，而值就是你所设置的对象。</p>]]></content:encoded>
            <category>Android</category>
            <category>面试</category>
        </item>
        <item>
            <title><![CDATA[AndroidManifest解析]]></title>
            <link>https://wawov.com/blog/2015/12/14/android-manifest-analysis</link>
            <guid>https://wawov.com/blog/2015/12/14/android-manifest-analysis</guid>
            <pubDate>Mon, 14 Dec 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[关于AndroidManifest.xml]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="关于androidmanifestxml">关于AndroidManifest.xml<a href="https://wawov.com/blog/2015/12/14/android-manifest-analysis#%E5%85%B3%E4%BA%8Eandroidmanifestxml" class="hash-link" aria-label="关于AndroidManifest.xml的直接链接" title="关于AndroidManifest.xml的直接链接">​</a></h4>
<p>AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录，描述了package中暴露的组件（activities, services, 等等），他们各自的实现类，各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services, 和Intent Receivers,还能指定permissions和instrumentation。</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="各个节点的详细介绍">各个节点的详细介绍<a href="https://wawov.com/blog/2015/12/14/android-manifest-analysis#%E5%90%84%E4%B8%AA%E8%8A%82%E7%82%B9%E7%9A%84%E8%AF%A6%E7%BB%86%E4%BB%8B%E7%BB%8D" class="hash-link" aria-label="各个节点的详细介绍的直接链接" title="各个节点的详细介绍的直接链接">​</a></h4>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="manifest属性">(&lt;manifest&gt;)属性<a href="https://wawov.com/blog/2015/12/14/android-manifest-analysis#manifest%E5%B1%9E%E6%80%A7" class="hash-link" aria-label="(<manifest>)属性的直接链接" title="(<manifest>)属性的直接链接">​</a></h5>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;manifest  xmlns:android="http://schemas.android.com/apk/res/android"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          package="com.woody.test"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:sharedUserId="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:sharedUserLabel="string resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:versionCode="integer"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:versionName="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:installLocation=["auto" | "internalOnly | preferExternal"] &gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;/manifest&gt;</span><br></span></code></pre></div></div>
<ul>
<li>sharedUserId，表明数据权限，因为默认情况下，Android给每个APK分配一个唯一的UserID，所以是默认禁止不同APK访问共享数据的。若要共享数据，第一可以采用Share Preference方法，第二种就可以采用sharedUserId了，将不同APK的sharedUserId都设为一样，则这些APK之间就可以互相共享数据了</li>
<li>sharedUserLabel，一个共享的用户名，它只有在设置了sharedUserId属性的前提下才会有意义</li>
<li>versionCode，给设备程序识别版本(升级)用的必须是一个interger值代表app更新过多少次</li>
<li>versionName，这个名称是给用户看的，你可以将你的APP版本号设置为1.1版</li>
<li>installLocation，安装参数，是Android2.2中的一个新特性，installLocation有三个值可以选择：internalOnly、auto、preferExternal。<!-- -->
<ul>
<li>preferExternal，系统会优先考虑将APK安装到SD卡上(当然最终用户可以选择为内部ROM存储上，如果SD存储已满，也会安装到内部存储上）</li>
<li>auto，系统将会根据存储空间自己去适应</li>
<li>internalOnly，是指必须安装到内部才能运行</li>
</ul>
</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="-application-属性">(&lt; Application &gt;)属性<a href="https://wawov.com/blog/2015/12/14/android-manifest-analysis#-application-%E5%B1%9E%E6%80%A7" class="hash-link" aria-label="(< Application >)属性的直接链接" title="(< Application >)属性的直接链接">​</a></h5>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;application  android:allowClearUserData=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:allowTaskReparenting=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:backupAgent="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:debuggable=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:description="string resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:enabled=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:hasCode=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:icon="drawable resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:killAfterRestore=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:label="string resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:manageSpaceActivity="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:name="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:permission="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:persistent=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:process="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:restoreAnyVersion=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:taskAffinity="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             android:theme="resource or theme" &gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;/application&gt;</span><br></span></code></pre></div></div>
<ul>
<li>android<!-- -->:allowClearUserData<!-- -->，用户是否能选择自行清除数据，默认为true，程序管理器包含一个选择允许用户清除数据。当为true时，用户可自己清理用户数据，反之亦然</li>
<li>android<!-- -->:allowTaskReparentin<!-- -->，是否允许activity更换从属的任务，比如从短信息任务切换到浏览器任务</li>
<li>android<!-- -->:debuggable<!-- -->，当设置为true时，表明该APP在手机上可以被调试。默认为false,在false的情况下调试该APP</li>
<li>android<!-- -->:description<!-- -->，可以用于具体描述获取该许可的程序可以做哪些事情</li>
<li>android<!-- -->:label<!-- -->，应用名称</li>
<li>android<!-- -->:enabled<!-- -->，Android系统是否能够实例化该应用程序的组件</li>
<li>android<!-- -->:icon<!-- -->，APP的图标</li>
<li>android<!-- -->:name<!-- -->，为应用程序所实现的Application子类的全名。当应用程序进程开始时，该类在所有应用程序组件之前被实例化</li>
<li>android<!-- -->:permission<!-- -->，这个属性若在&lt;application&gt;上定义的话，是一个给应用程序的所有组件设置许可的便捷方式</li>
<li>android<!-- -->:presistent<!-- -->，该应用程序是否应该在任何时候都保持运行状态,默认为false</li>
<li>android<!-- -->:process<!-- -->，应用程序运行的进程名，它的默认值为&lt;manifest&gt;元素里设置的包名，当然每个组件都可以通过设置该属性来覆盖默认值。如果你想两个应用程序共用一个进程的话，你可以设置他们的android<!-- -->:process<!-- -->相同，但前提条件是他们共享一个用户ID及被赋予了相同证书的时候</li>
<li>android<!-- -->:taskAffinity<!-- -->，拥有相同的affinity的Activity理论上属于相同的Task，应用程序默认的affinity的名字是&lt;manifest&gt;元素中设定的package名</li>
<li>android<!-- -->:theme<!-- -->，资源的风格</li>
</ul>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="-activity-属性">(&lt; Activity &gt;)属性<a href="https://wawov.com/blog/2015/12/14/android-manifest-analysis#-activity-%E5%B1%9E%E6%80%A7" class="hash-link" aria-label="(< Activity >)属性的直接链接" title="(< Activity >)属性的直接链接">​</a></h5>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;activity android:allowTaskReparenting=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:alwaysRetainTaskState=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:clearTaskOnLaunch=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:configChanges=["mcc", "mnc", "locale",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                 "touchscreen", "keyboard", "keyboardHidden",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                 "navigation", "orientation", "screenLayout",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                 "fontScale", "uiMode"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:enabled=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:excludeFromRecents=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:exported=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:finishOnTaskLaunch=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:icon="drawable resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:label="string resource"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:launchMode=["multiple" | "singleTop" |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                              "singleTask" | "singleInstance"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:multiprocess=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:name="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:noHistory=["true" | "false"]  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:permission="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:process="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:screenOrientation=["unspecified" | "user" | "behind" |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                     "landscape" | "portrait" |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                     "sensor" | "nosensor"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:stateNotNeeded=["true" | "false"]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:taskAffinity="string"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:theme="resource or theme"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          android:windowSoftInputMode=["stateUnspecified",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                       "stateUnchanged", "stateHidden",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                       "stateAlwaysHidden", "stateVisible",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                       "stateAlwaysVisible", "adjustUnspecified",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                       "adjustResize", "adjustPan"] &gt;   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;/activity&gt;</span><br></span></code></pre></div></div>
<ul>
<li>android<!-- -->:alwaysRetainTaskState<!-- -->，是否保留状态不变， 比如切换回home, 再从新打开，activity处于最后的状态。比如一个浏览器拥有很多状态(当打开了多个TAB的时候)，用户并不希望丢失这些状态时，此时可将此属性设置为true</li>
<li>android<!-- -->:clearTaskOnLaunch<!-- -->，比如 P 是 activity, Q 是被P 触发的 activity, 然后返回Home, 重新启动 P，是否显示 Q</li>
<li>android<!-- -->:configChanges<!-- -->，正常情况下. 如果手机旋转了.当前Activity后杀掉,然后根据方向重新加载这个Activity. 就会从onCreate开始重新加载.
如果你设置了 这个选项, 当手机旋转后,当前Activity之后调用onConfigurationChanged() 方法. 而不跑onCreate方法等</li>
<li>android<!-- -->:excludeFromRecents<!-- -->，是否可被显示在最近打开的activity列表里，默认是false</li>
<li>android<!-- -->:finishOnTaskLaunch<!-- -->，当用户重新启动这个任务的时候，是否关闭已打开的activity，默认是false，如果这个属性和allowTaskReparenting都是true,这个属性就是王牌。Activity的亲和力将被忽略。该Activity已经被摧毁并非re-parented</li>
<li>android<!-- -->:launchMode<!-- -->，在多Activity开发中，有可能是自己应用之间的Activity跳转，或者夹带其他应用的可复用Activity。可能会希望跳转到原来某个Activity实例，而不是产生大量重复的Activity，默认为standard<!-- -->
<ul>
<li>standard：就是intent将发送给新的实例，所以每次跳转都会生成新的activity</li>
<li>singleTop：也是发送新的实例，但不同standard的一点是，在请求的Activity正好位于栈顶时(配置成singleTop的Activity)，不会构造新的实例</li>
<li>singleTask：和后面的singleInstance都只创建一个实例，当intent到来，需要创建设置为singleTask的Activity的时候，系统会检查栈里面是否已经有该Activity的实例。如果有直接将intent发送给它</li>
<li>首先说明一下task这个概念，Task可以认为是一个栈，可放入多个Activity，比如启动一个应用，那么Android就创建了一个Task，然后启动这个应用的入口Activity，那在它的界面上调用其他的Activity也只是在这个task里面。singleInstance模式就是将该Activity单独放入一个栈中，这样这个栈中只有这一个Activity，不同应用的intent都由这个Activity接收和展示，这样就做到了共享。当然前提是这些应用都没有被销毁，所以刚才是按下的HOME键，如果按下了返回键，则无效</li>
</ul>
</li>
<li>android<!-- -->:multiprocess<!-- -->，是否允许多进程，默认是false</li>
</ul>]]></content:encoded>
            <category>Android</category>
        </item>
        <item>
            <title><![CDATA[july-class-01（字符和字符串）]]></title>
            <link>https://wawov.com/blog/2015/12/12/july-class-01</link>
            <guid>https://wawov.com/blog/2015/12/12/july-class-01</guid>
            <pubDate>Sat, 12 Dec 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[Huffman编码]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="huffman编码">Huffman编码<a href="https://wawov.com/blog/2015/12/12/july-class-01#huffman%E7%BC%96%E7%A0%81" class="hash-link" aria-label="Huffman编码的直接链接" title="Huffman编码的直接链接">​</a></h4>
<p>Huffman编码是一种无损压缩编码方案。</p>
<p>思想：根据源字符出现的概率对字符编码，概率高的字符使用较短的编码，概率低的使用较长的编码，从而使得编码后的字符串长度期望最小。</p>
<p>Huffman编码是一种贪心算法：每次总选择两个最小概率的字符节点合并</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="移除空格算法">移除空格算法<a href="https://wawov.com/blog/2015/12/12/july-class-01#%E7%A7%BB%E9%99%A4%E7%A9%BA%E6%A0%BC%E7%AE%97%E6%B3%95" class="hash-link" aria-label="移除空格算法的直接链接" title="移除空格算法的直接链接">​</a></h5>
<p>时间复杂度O(n)，空间复杂度O(1)</p>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#include &lt;iostream&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#include &lt;stdio.h&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using namespace std;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void RemoveBlank(char* pString){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    int j = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for(int i= 0;pString[i]!='\0';i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if(pString[i] != ' '){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            if(i != j){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               pString[j] = pString[i]; </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            j++;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pString[j] = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int main(){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    char str [] = "I have  Dream o";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    RemoveBlank(str);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    cout&lt;&lt;str&lt;&lt;endl;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="找大数">找大数<a href="https://wawov.com/blog/2015/12/12/july-class-01#%E6%89%BE%E5%A4%A7%E6%95%B0" class="hash-link" aria-label="找大数的直接链接" title="找大数的直接链接">​</a></h5>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#include &lt;iostream&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#include &lt;stdio.h&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using namespace std;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void FindMax(const int* a, int size,int&amp; nMax,int&amp; nSecondMax){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    for(int i = 0;i&lt;size;i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if(nMax &lt; a[i]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            nSecondMax = nMax;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            nMax = a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }else if(nSecondMax &lt; a[i]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            nSecondMax = a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int main(){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    int a [] = {1,5,4,8,3,2,9,14};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    int nMax=0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    int nSecondMax = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    FindMax(a,sizeof(a)/sizeof(int),nMax,nSecondMax);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    printf("max=%d,secondMax = %d\n",nMax,nSecondMax);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="huffman-编码java版">Huffman 编码Java版<a href="https://wawov.com/blog/2015/12/12/july-class-01#huffman-%E7%BC%96%E7%A0%81java%E7%89%88" class="hash-link" aria-label="Huffman 编码Java版的直接链接" title="Huffman 编码Java版的直接链接">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> public static class HuffmanNode {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		public HuffmanNode() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			// TODO Auto-generated constructor stub</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		public int weight = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		public int parent = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		public int leftNode = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		public int rightNode = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>
<p>mian函数</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public static void main(String[] args) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		// TODO Auto-generated method stub</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		String str = "when I was young I'd listen to the radio waiting for my favorite songs "</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "when they played I'd sing along,"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "it make me smile."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "those were such happy times and not so long ago"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "how I wondered where they'd gone."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "but they're back again just like a long lost friend"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "all the songs I love so well."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "every shalala every wo'wo"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "still shines."</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "every shing-a-ling-a-ling "</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				+ "that they're starting" + "to sing so fine";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int[] weight = new int[N];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		ArrayList&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		CalcFrequency(str, weight);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		weight[(int) '\t'] = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		CalcExistChar(weight, list);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int N2 = list.size();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		ArrayList&lt;ArrayList&lt;Character&gt;&gt; node = new ArrayList&lt;ArrayList&lt;Character&gt;&gt;(N2);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		HuffmanCoding(weight, N2, node);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		printNode(node,list);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		System.out.println("complete");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>
<p>计算字符权重</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">	static void CalcFrequency(String str, int[] arr) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			for (int i = 0; i &lt; str.length(); i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				arr[(int) str.charAt(i)]++;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>
<p>将字符记录在list中</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">	static void CalcExistChar(int[] arr, ArrayList&lt;Integer&gt; list) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int j = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; arr.length; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if (arr[i] != 0) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				list.add(i);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				if (i != j) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					arr[j] = arr[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					j++;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>
<p>核心代码</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">static void HuffmanCoding(int[] weight, int N,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			ArrayList&lt;ArrayList&lt;Character&gt;&gt; node) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if (N &lt;= 0)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int m = 2 * N + 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		// 初始化树</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		ArrayList&lt;HuffmanNode&gt; huffmanTree = new ArrayList&lt;HuffmanNode&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; m; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			HuffmanNode huffmanNode = new HuffmanNode();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.add(huffmanNode);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; N; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.get(i).weight = weight[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		// 找出2个权重最小将其组合</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = N; i &lt; m; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			FindMin(huffmanTree, i);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if (s1 == -1 || s2 == -1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				continue;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.get(s1).parent = huffmanTree.get(s2).parent = i;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.get(i).leftNode = s1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.get(i).rightNode = s2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			huffmanTree.get(i).weight = huffmanTree.get(s1).weight</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					+ huffmanTree.get(s2).weight;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		//根据建好的huffman树从叶子到根，计算每个叶子节点的编码</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int nParent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int curNode;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; N; i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			ArrayList&lt;Character&gt; cur = new ArrayList&lt;Character&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			nParent = huffmanTree.get(i).parent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			curNode = i;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			while (nParent != 0) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				if (huffmanTree.get(nParent).leftNode == curNode) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					cur.add('0');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				} else {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					cur.add('1');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				curNode = nParent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				nParent = huffmanTree.get(curNode).parent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			Collections.reverse(cur);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			node.add(cur);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>
<p>打印树编码</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">	static void printNode(ArrayList&lt;ArrayList&lt;Character&gt;&gt; code,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			ArrayList&lt;Integer&gt; charList) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; code.size(); i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			printCode((char) charList.get(i).intValue(), code.get(i));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	static void printCode(char c, ArrayList&lt;Character&gt; code) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		System.out.println("char=" + c + "：");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for (int i = 0; i &lt; code.size(); i++) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			System.out.print(code.get(i));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		System.out.println();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>算法</category>
        </item>
        <item>
            <title><![CDATA[july-class-02（树）]]></title>
            <link>https://wawov.com/blog/2015/12/12/july-class-02</link>
            <guid>https://wawov.com/blog/2015/12/12/july-class-02</guid>
            <pubDate>Sat, 12 Dec 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[二叉树遍历]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="二叉树遍历">二叉树遍历<a href="https://wawov.com/blog/2015/12/12/july-class-02#%E4%BA%8C%E5%8F%89%E6%A0%91%E9%81%8D%E5%8E%86" class="hash-link" aria-label="二叉树遍历的直接链接" title="二叉树遍历的直接链接">​</a></h4>
<p>前序遍历：根左右
中序遍历：左根右
后序遍历：左右根</p>
<p>层次遍历：使用队列</p>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//先序遍历</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void PreOrderTraverse(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		printf("%c",T-&gt;data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		PreOrderTraverse(T-&gt;lchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		PreOrderTraverse(T-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//中序遍历</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void InOrderTraverse(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		InOrderTraverse(T-&gt;lchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		printf("%c",T-&gt;data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		InOrderTraverse(T-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//后序遍历</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void PostOrderTraverse(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		PostOrderTraverse(T-&gt;lchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		PostOrderTraverse(T-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		printf("%c",T-&gt;data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="二叉树的深度">二叉树的深度<a href="https://wawov.com/blog/2015/12/12/july-class-02#%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%B7%B1%E5%BA%A6" class="hash-link" aria-label="二叉树的深度的直接链接" title="二叉树的深度的直接链接">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">int Depth(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T) return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int d1=Depth(T-&gt;lchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int d2=Depth(T-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return (d1&gt;d2?d1:d2)+1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="二叉树的节点数">二叉树的节点数<a href="https://wawov.com/blog/2015/12/12/july-class-02#%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%8A%82%E7%82%B9%E6%95%B0" class="hash-link" aria-label="二叉树的节点数的直接链接" title="二叉树的节点数的直接链接">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">int NodeCount(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T) return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return NodeCount(T-&gt;lchild)+NodeCount(T-&gt;rchild)+1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="二叉树的叶子节点数">二叉树的叶子节点数<a href="https://wawov.com/blog/2015/12/12/july-class-02#%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%8F%B6%E5%AD%90%E8%8A%82%E7%82%B9%E6%95%B0" class="hash-link" aria-label="二叉树的叶子节点数的直接链接" title="二叉树的叶子节点数的直接链接">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">int LeafCount(BiTree T){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T) return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T-&gt;lchild &amp;&amp; !T-&gt;rchild) return 1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return LeafCount(T-&gt;lchild)+LeafCount(T-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="判断两颗二叉树是否相同">判断两颗二叉树是否相同<a href="https://wawov.com/blog/2015/12/12/july-class-02#%E5%88%A4%E6%96%AD%E4%B8%A4%E9%A2%97%E4%BA%8C%E5%8F%89%E6%A0%91%E6%98%AF%E5%90%A6%E7%9B%B8%E5%90%8C" class="hash-link" aria-label="判断两颗二叉树是否相同的直接链接" title="判断两颗二叉树是否相同的直接链接">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">bool IsSameTree(BiTree T1,BiTree T2){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T1 &amp;&amp; !T2) return true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(!T1 || !T2) return false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(T1-&gt;data!=T2-&gt;data) return false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return IsSameTree(T1-&gt;lchild,T2-&gt;lchild)&amp;&amp;IsSameTree(T1-&gt;rchild,T2-&gt;rchild);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>算法</category>
        </item>
        <item>
            <title><![CDATA[july-class-03（查找与排序）]]></title>
            <link>https://wawov.com/blog/2015/12/12/july-class-03</link>
            <guid>https://wawov.com/blog/2015/12/12/july-class-03</guid>
            <pubDate>Sat, 12 Dec 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[折半查找]]></description>
            <content:encoded><![CDATA[<h4 class="anchor anchorWithStickyNavbar_LWe7" id="折半查找">折半查找<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E6%8A%98%E5%8D%8A%E6%9F%A5%E6%89%BE" class="hash-link" aria-label="折半查找的直接链接" title="折半查找的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">int BinarySearch(int *a,int n,int key){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int low=1,high=n,mid;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	while(low&lt;=high){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		mid=(low+high)/2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(key&lt;a[mid]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			high=mid-1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}else if(key&gt;a[mid]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			low=mid+1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}else{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return mid;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="冒泡排序">冒泡排序<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="冒泡排序的直接链接" title="冒泡排序的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">void BubbleSort(int *a,int n){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int i,j,flag;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	for(i=1;i&lt;n;i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		flag=0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for(j=0;j&lt;n-i;j++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if(a[j]&gt;a[j+1]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[j]=a[j]^a[j+1];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[j+1]=a[j]^a[j+1];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[j]=a[j]^a[j+1];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				flag=1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(flag==0) break;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="快速排序">快速排序<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="快速排序的直接链接" title="快速排序的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">void QuickSort(int *a,int low,int high){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if(low&lt;high){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		int pivotloc=Partition(a,low,high);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		QuickSort(a,low,pivotloc-1);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		QuickSort(a,pivotloc+1,high);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">int Partition(int *a,int low,int high){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int pivotkey=a[low];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	while(low&lt;high){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		while(low&lt;high &amp;&amp; a[high]&gt;=pivotkey) --high;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[low]=a[high];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		while(low&lt;high &amp;&amp; a[low]&lt;=pivotkey) ++low;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[high]=a[low];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	a[low]=pivotkey;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return low;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="直接插入排序">直接插入排序<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E7%9B%B4%E6%8E%A5%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="直接插入排序的直接链接" title="直接插入排序的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">void InsertSort(int *a,int n){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int i,j;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	for(i=2;i&lt;=n;i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(a[i]&lt;a[i-1]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			a[0]=a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			for(j=i-1;a[0]&lt;a[j];j--){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[j+1]=a[j];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			a[j+1]=a[0];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="希尔排序">希尔排序<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="希尔排序的直接链接" title="希尔排序的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">void ShellSort(int *a,int n){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int i,j;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int dk=n/2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	while(dk&gt;=1){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		for(i=dk+1;i&lt;=n;i++){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			if(a[i]&lt;a[i-dk]){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[0]=a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				for(j=i-dk;j&gt;0 &amp;&amp; a[0]&lt;a[j];j-=dk){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					a[j+dk]=a[j];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				a[j+dk]=a[0];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		dk=dk/2;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="堆排序">堆排序<a href="https://wawov.com/blog/2015/12/12/july-class-03#%E5%A0%86%E6%8E%92%E5%BA%8F" class="hash-link" aria-label="堆排序的直接链接" title="堆排序的直接链接">​</a></h4>
<div class="language-c++ codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c++ codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">void HeapAdjust(int *a,int s,int m){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int rc=a[s];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	for(int j=2*s;j&lt;=m;j*=2){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(j&lt;m &amp;&amp; a[j]&lt;a[j+1]) j++;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if(rc&gt;=a[j]) break;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[s]=a[j];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		s=j;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	a[s]=rc;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">void HeapSort(int *a,int n){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	int i;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	for(i=n/2;i&gt;0;i--){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		HeapAdjust(a,i,n);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	for(i=n;i&gt;1;i--){</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[1]=a[1]^a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[i]=a[1]^a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		a[1]=a[1]^a[i];</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		HeapAdjust(a,1,i-1);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>算法</category>
        </item>
    </channel>
</rss>