<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://hylinux1024.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hylinux1024.github.io/" rel="alternate" type="text/html" /><updated>2026-05-19T04:14:22+00:00</updated><id>https://hylinux1024.github.io/feed.xml</id><title type="html">Thinking Tech</title><subtitle>Personal Website</subtitle><author><name>hylinux</name></author><entry><title type="html">一份程序猿单词列表（updating）</title><link href="https://hylinux1024.github.io/%E4%B8%80%E4%BB%BD%E7%A8%8B%E5%BA%8F%E7%8C%BF%E5%8D%95%E8%AF%8D%E5%88%97%E8%A1%A8-updating" rel="alternate" type="text/html" title="一份程序猿单词列表（updating）" /><published>2018-11-03T00:00:00+00:00</published><updated>2018-11-03T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E4%B8%80%E4%BB%BD%E7%A8%8B%E5%BA%8F%E7%8C%BF%E5%8D%95%E8%AF%8D%E5%88%97%E8%A1%A8%EF%BC%88updating%EF%BC%89</id><content type="html" xml:base="https://hylinux1024.github.io/%E4%B8%80%E4%BB%BD%E7%A8%8B%E5%BA%8F%E7%8C%BF%E5%8D%95%E8%AF%8D%E5%88%97%E8%A1%A8-updating"><![CDATA[<p>以下单词是个人平时阅读英文文档时遇到的一些“生”单词。这些单词是在平时中积累下来的，也都是通过Google进行翻译的，所以有些翻译可能不是特别准确，毕竟不是字典，只作为平时的一个积累，偶尔翻阅，扩展自己的单词量。</p>

<p>该文档将持续更新，可以关注<a href="https://github.com/hylinux1024/word-list-for-programmer">https://github.com/hylinux1024/word-list-for-programmer</a></p>

<h4 id="a">A</h4>

<p><code class="language-plaintext highlighter-rouge">a variety of</code> 各种各样的；</p>

<p><code class="language-plaintext highlighter-rouge">aggressively</code> 积极地；侵略地；激烈地；adv；</p>

<p><code class="language-plaintext highlighter-rouge">affiliate</code> 使率属；v；</p>

<p><code class="language-plaintext highlighter-rouge">amortized</code> 摊销；分期付款；v；<code class="language-plaintext highlighter-rouge">amortized constant time</code> 均摊不变时间；</p>

<p><code class="language-plaintext highlighter-rouge">accidental</code> 偶然；偶发；非主要的；adj；</p>

<p><code class="language-plaintext highlighter-rouge">arbitrary</code> 武断的；adj；<code class="language-plaintext highlighter-rouge">arbitrary number</code> 任意数字；</p>

<p><code class="language-plaintext highlighter-rouge">architecture</code> 框架；n；</p>

<p><code class="language-plaintext highlighter-rouge">augment</code> 增加；v；</p>

<h4 id="b">B</h4>

<p><code class="language-plaintext highlighter-rouge">backend</code> 后端；n；<code class="language-plaintext highlighter-rouge">backend developer</code> 后端开发人员；</p>

<p><code class="language-plaintext highlighter-rouge">hierarchy</code> 等级制度；层级；n；</p>

<p><code class="language-plaintext highlighter-rouge">blend</code> 混合；v；<code class="language-plaintext highlighter-rouge">to blend A and B</code> 把A和B混合起来；</p>

<p><code class="language-plaintext highlighter-rouge">bootcamp</code> 训练营；集训；n；</p>

<h4 id="c">C</h4>

<p><code class="language-plaintext highlighter-rouge">coroutines</code> 协程；n；</p>

<p><code class="language-plaintext highlighter-rouge">creepy</code> 使人心里发毛的；adj；</p>

<p><code class="language-plaintext highlighter-rouge">correctness</code> 正确性；n；</p>

<p><code class="language-plaintext highlighter-rouge">credentials</code> 证书；凭据；n；</p>

<p><code class="language-plaintext highlighter-rouge">contrive</code> 设法促成；v；</p>

<h4 id="d">D</h4>

<p><code class="language-plaintext highlighter-rouge">decouples</code> 解耦；v；</p>

<p><code class="language-plaintext highlighter-rouge">dramatically</code> 显著地；戏剧性地；adv；</p>

<h4 id="e">E</h4>

<p><code class="language-plaintext highlighter-rouge">exemplary</code> 可作楷模的；adv；</p>

<p><code class="language-plaintext highlighter-rouge">encapsulate</code> 封装；v；</p>

<p><code class="language-plaintext highlighter-rouge">entrepreneurship</code> 创业；n；</p>

<h4 id="f">F</h4>

<p><code class="language-plaintext highlighter-rouge">formulate</code> 构想；v；</p>

<p><code class="language-plaintext highlighter-rouge">facility</code> 场所；n；<code class="language-plaintext highlighter-rouge">a health-care facility</code> 保健中心；</p>

<p><code class="language-plaintext highlighter-rouge">facilitate</code> 促进；使…便利；v；</p>

<h4 id="g">G</h4>

<p><code class="language-plaintext highlighter-rouge">gratuitous</code> 不必要的；无理由的；免费的；adj；</p>

<h4 id="h">H</h4>

<h4 id="i">I</h4>

<p><code class="language-plaintext highlighter-rouge">inherently</code> 内在的；adv；</p>

<p><code class="language-plaintext highlighter-rouge">impede</code> 阻碍；妨碍；拖延；v；</p>

<p><code class="language-plaintext highlighter-rouge">inversion</code> 倒转；颠倒；n；</p>

<p><code class="language-plaintext highlighter-rouge">interoperability</code>互通性；n；</p>

<p><code class="language-plaintext highlighter-rouge">interpolate</code> 插入；添加；v；</p>

<p><code class="language-plaintext highlighter-rouge">interstitial</code> 空隙的；间质性；n；</p>

<p><code class="language-plaintext highlighter-rouge">in the absence of</code> 在缺少…之下；</p>

<p><code class="language-plaintext highlighter-rouge">interchangeable</code> 通用；adj；</p>

<p><code class="language-plaintext highlighter-rouge">interoperability</code> 互通性；n；</p>

<p><code class="language-plaintext highlighter-rouge">infrastructure</code> 基础设施；n；</p>

<h4 id="j">J</h4>

<h4 id="k">K</h4>

<p><code class="language-plaintext highlighter-rouge">keystroke</code> 按键；n；</p>

<h4 id="l">L</h4>

<p><code class="language-plaintext highlighter-rouge">likewise</code> 同样地；adv；</p>

<p><code class="language-plaintext highlighter-rouge">lower latency</code> 低延迟；</p>

<h4 id="m">M</h4>

<p><code class="language-plaintext highlighter-rouge">miniature</code> 微型；雏形；adj；</p>

<p><code class="language-plaintext highlighter-rouge">mutate</code> 变化；v；</p>

<h4 id="n">N</h4>

<h4 id="o">O</h4>

<p><code class="language-plaintext highlighter-rouge">overlap</code> 叠加；v；</p>

<h4 id="p">P</h4>

<p><code class="language-plaintext highlighter-rouge">primitive</code> 原语；n；原始的；adj；<code class="language-plaintext highlighter-rouge">primitive man</code> 原始人；</p>

<p><code class="language-plaintext highlighter-rouge">pragmatic</code> 务实的；adj；</p>

<p><code class="language-plaintext highlighter-rouge">permits</code> 容许；许可；v；</p>

<p><code class="language-plaintext highlighter-rouge">pipeline</code> 管道；n；<code class="language-plaintext highlighter-rouge">to lay a gas pipeline</code> 铺设煤气管道；</p>

<p><code class="language-plaintext highlighter-rouge">profile</code> 侧面；轮廓；n；<code class="language-plaintext highlighter-rouge">profiling</code> 剖析；描绘…轮廓；v；<code class="language-plaintext highlighter-rouge">profiler</code> 探查(器)、检测器；n；</p>

<p><code class="language-plaintext highlighter-rouge">payload</code> 装载量；有效载荷；n；<code class="language-plaintext highlighter-rouge">network payloads</code> 网络负载；</p>

<p><code class="language-plaintext highlighter-rouge">portfolio</code> 公事包；投资组合；n；</p>

<h4 id="q">Q</h4>

<h4 id="r">R</h4>

<h4 id="s">S</h4>

<p><code class="language-plaintext highlighter-rouge">scenario</code> 剧本；概要；场景；n；</p>

<p><code class="language-plaintext highlighter-rouge">semantics</code> 语义(学)；n；</p>

<p><code class="language-plaintext highlighter-rouge">specification</code> 规范；n；</p>

<p><code class="language-plaintext highlighter-rouge">stale</code> 陈旧；不新鲜；adj；</p>

<p><code class="language-plaintext highlighter-rouge">simultaneously</code> 同时；adv；</p>

<p><code class="language-plaintext highlighter-rouge">spin</code> 转动；v；</p>

<h4 id="t">T</h4>

<p><code class="language-plaintext highlighter-rouge">traverse the list</code> 遍历列表；</p>

<h4 id="u">U</h4>

<p><code class="language-plaintext highlighter-rouge">utterly</code> 完全；万般；adj；</p>

<p><code class="language-plaintext highlighter-rouge">unsynchronized</code> 不同步；adj；</p>

<p><code class="language-plaintext highlighter-rouge">unmodifiable</code> 不可修改；adj；</p>

<p><code class="language-plaintext highlighter-rouge">unadulterated</code> 纯正的；adj；</p>

<h4 id="v">V</h4>

<p><code class="language-plaintext highlighter-rouge">verbosity</code> 啰嗦；冗长；n；</p>

<h4 id="w">W</h4>

<p><code class="language-plaintext highlighter-rouge">withhold</code> 扣押；保留；v；</p>

<h4 id="x">X</h4>

<h4 id="y">Y</h4>

<h4 id="z">Z</h4>]]></content><author><name>hylinux</name></author><category term="个人学习" /><summary type="html"><![CDATA[以下单词是个人平时阅读英文文档时遇到的一些“生”单词，该文档将持续更新。]]></summary></entry><entry><title type="html">源码分析Retrofit请求流程</title><link href="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90Retrofit%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B" rel="alternate" type="text/html" title="源码分析Retrofit请求流程" /><published>2018-11-03T00:00:00+00:00</published><updated>2018-11-03T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90Retrofit%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B</id><content type="html" xml:base="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90Retrofit%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B"><![CDATA[<p><code class="language-plaintext highlighter-rouge">Retrofit</code> 是 <code class="language-plaintext highlighter-rouge">square</code> 公司的另一款广泛流行的网络请求框架。前面的一篇文章《源码分析OKHttp执行过程》已经对 <code class="language-plaintext highlighter-rouge">OkHttp</code> 网络请求框架有一个大概的了解。今天同样地对 <code class="language-plaintext highlighter-rouge">Retrofit</code> 的源码进行走读，对其底层的实现逻辑做到心中有数。</p>

<h4 id="0x00-基本用法">0x00 基本用法</h4>

<p><code class="language-plaintext highlighter-rouge">Retrofit</code> 的项目地址为：https://github.com/square/retrofit</p>

<p>打开项目目录下的 <code class="language-plaintext highlighter-rouge">samples</code> 文件夹，从这里可以浏览 <code class="language-plaintext highlighter-rouge">Retrofit</code> 项目的使用范例。</p>

<p><img src="../images/retrofit-samples.png" alt="" /></p>

<p>在本文中打开<code class="language-plaintext highlighter-rouge">SimpleService.java</code> 这个类作为源码走读的入口。这个类很简单，展示了 <code class="language-plaintext highlighter-rouge">Retrofit</code> 的基本用法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">SimpleService</span> <span class="o">{</span>
  <span class="c1">//定义接口请求地址</span>
  <span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">API_URL</span> <span class="o">=</span> <span class="s">"https://api.github.com"</span><span class="o">;</span>
  <span class="c1">//定义接口返回数据的实体类</span>
  <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Contributor</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">login</span><span class="o">;</span>
    <span class="kd">public</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">contributions</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">Contributor</span><span class="o">(</span><span class="nc">String</span> <span class="n">login</span><span class="o">,</span> <span class="kt">int</span> <span class="n">contributions</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">this</span><span class="o">.</span><span class="na">login</span> <span class="o">=</span> <span class="n">login</span><span class="o">;</span>
      <span class="k">this</span><span class="o">.</span><span class="na">contributions</span> <span class="o">=</span> <span class="n">contributions</span><span class="o">;</span>
    <span class="o">}</span>
  <span class="o">}</span>
  <span class="c1">//定义网络请求接口</span>
  <span class="kd">public</span> <span class="kd">interface</span> <span class="nc">GitHub</span> <span class="o">{</span>
    <span class="c1">//这个是请求github项目代码贡献者列表的接口</span>
    <span class="c1">//使用@GET注解指定GET请求，并指定接口请求路径，使用大括号{}定义的参数，是形参，retrofit会把方法中的</span>
    <span class="c1">//@Path 传入到请求路径中</span>
    <span class="nd">@GET</span><span class="o">(</span><span class="s">"/repos/{owner}/{repo}/contributors"</span><span class="o">)</span>
    <span class="nc">Call</span><span class="o">&lt;</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;&gt;</span> <span class="nf">contributors</span><span class="o">(</span>
        <span class="nd">@Path</span><span class="o">(</span><span class="s">"owner"</span><span class="o">)</span> <span class="nc">String</span> <span class="n">owner</span><span class="o">,</span>
        <span class="nd">@Path</span><span class="o">(</span><span class="s">"repo"</span><span class="o">)</span> <span class="nc">String</span> <span class="n">repo</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">...</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="c1">// 创建一个retrofit，并且指定了接口的baseUrl</span>
    <span class="c1">// 然后设置了一个gson转换器，用于将接口请求下来的json字符串转换为Contributor实体类。</span>
    <span class="nc">Retrofit</span> <span class="n">retrofit</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Retrofit</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
        <span class="o">.</span><span class="na">baseUrl</span><span class="o">(</span><span class="no">API_URL</span><span class="o">)</span>
        <span class="o">.</span><span class="na">addConverterFactory</span><span class="o">(</span><span class="nc">GsonConverterFactory</span><span class="o">.</span><span class="na">create</span><span class="o">())</span>
        <span class="o">.</span><span class="na">build</span><span class="o">();</span>

    <span class="c1">// 这里是魔法所在，retrofit将程序猿定义的接口变成“实现类”</span>
    <span class="nc">GitHub</span> <span class="n">github</span> <span class="o">=</span> <span class="n">retrofit</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nc">GitHub</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>

    <span class="c1">//通过retrofit这个“实现类”执行contributors方法</span>
    <span class="nc">Call</span><span class="o">&lt;</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;&gt;</span> <span class="n">call</span> <span class="o">=</span> <span class="n">github</span><span class="o">.</span><span class="na">contributors</span><span class="o">(</span><span class="s">"square"</span><span class="o">,</span> <span class="s">"retrofit"</span><span class="o">);</span>

    <span class="c1">// 执行Call类中的execute方法，这是一个同步方法</span>
    <span class="c1">// 当然跟okhttp一样，异步方法是enqueue，这个下文会提到</span>
    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;</span> <span class="n">contributors</span> <span class="o">=</span> <span class="n">call</span><span class="o">.</span><span class="na">execute</span><span class="o">().</span><span class="na">body</span><span class="o">();</span>
    <span class="k">for</span> <span class="o">(</span><span class="nc">Contributor</span> <span class="n">contributor</span> <span class="o">:</span> <span class="n">contributors</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">contributor</span><span class="o">.</span><span class="na">login</span> <span class="o">+</span> <span class="s">" ("</span> <span class="o">+</span> <span class="n">contributor</span><span class="o">.</span><span class="na">contributions</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
    <span class="o">}</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>通过上面代码的阅读，知道 <code class="language-plaintext highlighter-rouge">retrofit</code> 使用流程</p>

<ol>
  <li>定义 <code class="language-plaintext highlighter-rouge">API</code></li>
  <li>构造接口数据实体类</li>
  <li>构造 <code class="language-plaintext highlighter-rouge">retrofit</code> 对象，指定<code class="language-plaintext highlighter-rouge">baseUrl</code>和数据转换器（即接口数据解析器，如对<code class="language-plaintext highlighter-rouge">json</code>、<code class="language-plaintext highlighter-rouge">xml</code>、<code class="language-plaintext highlighter-rouge">protobuf</code>等数据类型的解析）</li>
  <li>通过 <code class="language-plaintext highlighter-rouge">retrofit</code> 将程序猿定义的 <code class="language-plaintext highlighter-rouge">API</code> 接口变成”实现类”</li>
  <li>执行“实现类”的方法</li>
  <li>执行网络请求，获取接口请求数据</li>
</ol>

<p>这个流程关键点是4、5、6，下文将详细对这几个步骤的源码进行阅读。</p>

<p>在继续下文之前，我们先看看这个<code class="language-plaintext highlighter-rouge">SimpleService</code>的执行结果，它打印了<code class="language-plaintext highlighter-rouge">retrofit</code> 这个项目的代码贡献者</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">JakeWharton</span> <span class="o">(</span><span class="mi">928</span><span class="o">)</span>
<span class="n">swankjesse</span> <span class="o">(</span><span class="mi">240</span><span class="o">)</span>
<span class="n">pforhan</span> <span class="o">(</span><span class="mi">48</span><span class="o">)</span>
<span class="n">eburke</span> <span class="o">(</span><span class="mi">36</span><span class="o">)</span>
<span class="n">dnkoutso</span> <span class="o">(</span><span class="mi">26</span><span class="o">)</span>
<span class="nc">NightlyNexus</span> <span class="o">(</span><span class="mi">26</span><span class="o">)</span>
<span class="n">edenman</span> <span class="o">(</span><span class="mi">24</span><span class="o">)</span>
<span class="n">loganj</span> <span class="o">(</span><span class="mi">17</span><span class="o">)</span>
<span class="nc">Noel</span><span class="o">-</span><span class="mi">96</span> <span class="o">(</span><span class="mi">16</span><span class="o">)</span>
<span class="n">rcdickerson</span> <span class="o">(</span><span class="mi">14</span><span class="o">)</span>
<span class="n">rjrjr</span> <span class="o">(</span><span class="mi">13</span><span class="o">)</span>
<span class="n">kryali</span> <span class="o">(</span><span class="mi">9</span><span class="o">)</span>
<span class="n">adriancole</span> <span class="o">(</span><span class="mi">9</span><span class="o">)</span>
<span class="n">holmes</span> <span class="o">(</span><span class="mi">7</span><span class="o">)</span>
<span class="n">swanson</span> <span class="o">(</span><span class="mi">7</span><span class="o">)</span>
<span class="nc">JayNewstrom</span> <span class="o">(</span><span class="mi">6</span><span class="o">)</span>
<span class="n">crazybob</span> <span class="o">(</span><span class="mi">6</span><span class="o">)</span>
<span class="nc">Jawnnypoo</span> <span class="o">(</span><span class="mi">6</span><span class="o">)</span>
<span class="n">danrice</span><span class="o">-</span><span class="n">square</span> <span class="o">(</span><span class="mi">5</span><span class="o">)</span>
<span class="n">vanniktech</span> <span class="o">(</span><span class="mi">5</span><span class="o">)</span>
<span class="nc">Turbo87</span> <span class="o">(</span><span class="mi">5</span><span class="o">)</span>
<span class="n">naturalwarren</span> <span class="o">(</span><span class="mi">5</span><span class="o">)</span>
<span class="n">guptasourabh04</span> <span class="o">(</span><span class="mi">4</span><span class="o">)</span>
<span class="n">artem</span><span class="o">-</span><span class="n">zinnatullin</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="n">codebutler</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="n">icastell</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="n">jjNford</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="n">f2prateek</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="nc">PromanSEW</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
<span class="n">koalahamlet</span> <span class="o">(</span><span class="mi">3</span><span class="o">)</span>
</code></pre></div></div>

<h4 id="0x01-构造过程">0x01 构造过程</h4>

<p>从上文的源码阅读中，可以看出<strong>程序猿只是定义了一个接口，但是现在实现接口的工作是由 <code class="language-plaintext highlighter-rouge">retrofit</code> 来实现的</strong></p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">GitHub</span> <span class="n">github</span> <span class="o">=</span> <span class="n">retrofit</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nc">GitHub</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>

<span class="nc">Call</span><span class="o">&lt;</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;&gt;</span> <span class="n">call</span> <span class="o">=</span> <span class="n">github</span><span class="o">.</span><span class="na">contributors</span><span class="o">(</span><span class="s">"square"</span><span class="o">,</span> <span class="s">"retrofit"</span><span class="o">);</span>
</code></pre></div></div>

<h5 id="create">create</h5>

<p>打开 <code class="language-plaintext highlighter-rouge">retrofit.create</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="no">T</span> <span class="nf">create</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Class</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">service</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//对接口进行校验</span>
    <span class="nc">Utils</span><span class="o">.</span><span class="na">validateServiceInterface</span><span class="o">(</span><span class="n">service</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">validateEagerly</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">eagerlyValidateMethods</span><span class="o">(</span><span class="n">service</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="c1">//通过Proxy创建了一个代理</span>
    <span class="k">return</span> <span class="o">(</span><span class="no">T</span><span class="o">)</span> <span class="nc">Proxy</span><span class="o">.</span><span class="na">newProxyInstance</span><span class="o">(</span><span class="n">service</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">(),</span> <span class="k">new</span> <span class="nc">Class</span><span class="o">&lt;?&gt;[]</span> <span class="o">{</span> <span class="n">service</span> <span class="o">},</span>
        <span class="k">new</span> <span class="nf">InvocationHandler</span><span class="o">()</span> <span class="o">{</span>
          <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Platform</span> <span class="n">platform</span> <span class="o">=</span> <span class="nc">Platform</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
          <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">emptyArgs</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Object</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>

          <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="nc">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="nc">Method</span> <span class="n">method</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
              <span class="kd">throws</span> <span class="nc">Throwable</span> <span class="o">{</span>
            <span class="c1">// If the method is a method from Object then defer to normal invocation.</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getDeclaringClass</span><span class="o">()</span> <span class="o">==</span> <span class="nc">Object</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
              <span class="k">return</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
            <span class="o">}</span>
            <span class="c1">//判断是否为默认方法，Java8中接口也可以有默认方法，所以这里有这个判断</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">platform</span><span class="o">.</span><span class="na">isDefaultMethod</span><span class="o">(</span><span class="n">method</span><span class="o">))</span> <span class="o">{</span>
              <span class="k">return</span> <span class="n">platform</span><span class="o">.</span><span class="na">invokeDefaultMethod</span><span class="o">(</span><span class="n">method</span><span class="o">,</span> <span class="n">service</span><span class="o">,</span> <span class="n">proxy</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
            <span class="o">}</span>
            <span class="c1">//关键点</span>
            <span class="k">return</span> <span class="nf">loadServiceMethod</span><span class="o">(</span><span class="n">method</span><span class="o">).</span><span class="na">invoke</span><span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">args</span> <span class="o">:</span> <span class="n">emptyArgs</span><span class="o">);</span>
          <span class="o">}</span>
        <span class="o">});</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>这个方法很短，关键是通过 <code class="language-plaintext highlighter-rouge">Proxy</code> 创建了一个 <code class="language-plaintext highlighter-rouge">Github</code> 接口的代理类并返回该代理。</p>

<p><code class="language-plaintext highlighter-rouge">newProxyInstance</code> 方法需要3个参数：<code class="language-plaintext highlighter-rouge">ClassLoader</code>、<code class="language-plaintext highlighter-rouge">Class&lt;?&gt;</code>数组、<code class="language-plaintext highlighter-rouge">InvocationHandler</code> 回调。</p>

<p>这个 <code class="language-plaintext highlighter-rouge">InvocationHandler</code> 非常关键，当执行接口 <code class="language-plaintext highlighter-rouge">Github</code> 的<code class="language-plaintext highlighter-rouge">contributors</code>方法时，会委托给<code class="language-plaintext highlighter-rouge">InvocationHandler</code>的<code class="language-plaintext highlighter-rouge">invoke</code> 方法来执行。即<code class="language-plaintext highlighter-rouge">Github</code>将接口代理给了<code class="language-plaintext highlighter-rouge">Proxy</code>来执行了。</p>

<h5 id="invocationhandler">InvocationHandler</h5>

<p>接着看<code class="language-plaintext highlighter-rouge">InvocationHandler</code> 接口的实现。</p>

<p>在 <code class="language-plaintext highlighter-rouge">invoke</code> 方法中有三个参数，其中<code class="language-plaintext highlighter-rouge">proxy</code> 就是代理对象，而 <code class="language-plaintext highlighter-rouge">method</code> 就是程序猿定义的那个网络请求接口，顾名思义 <code class="language-plaintext highlighter-rouge">args</code> 就是方法的参数。</p>

<p>此方法最终是调用了</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loadServiceMethod</span><span class="o">(</span><span class="n">method</span><span class="o">).</span><span class="na">invoke</span><span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">args</span> <span class="o">:</span> <span class="n">emptyArgs</span><span class="o">);</span>
</code></pre></div></div>

<h5 id="loadservicemethod">loadServiceMethod</h5>

<p>打开 <code class="language-plaintext highlighter-rouge">loadServiceMethod</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ServiceMethod</span><span class="o">&lt;?&gt;</span> <span class="n">loadServiceMethod</span><span class="o">(</span><span class="nc">Method</span> <span class="n">method</span><span class="o">)</span> <span class="o">{</span>
  <span class="c1">// 判断是否有缓存</span>
  <span class="nc">ServiceMethod</span><span class="o">&lt;?&gt;</span> <span class="n">result</span> <span class="o">=</span> <span class="n">serviceMethodCache</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">method</span><span class="o">);</span>
  <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
  <span class="c1">//同步处理</span>
  <span class="kd">synchronized</span> <span class="o">(</span><span class="n">serviceMethodCache</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">serviceMethodCache</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">method</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
      <span class="c1">//没有获取到缓存则使用`ServiceMethod`方法来创建</span>
      <span class="n">result</span> <span class="o">=</span> <span class="nc">ServiceMethod</span><span class="o">.</span><span class="na">parseAnnotations</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">method</span><span class="o">);</span>
      <span class="c1">//最后缓存起来</span>
      <span class="n">serviceMethodCache</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">method</span><span class="o">,</span> <span class="n">result</span><span class="o">);</span>
    <span class="o">}</span>
  <span class="o">}</span>
  <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个方法就是通过 <code class="language-plaintext highlighter-rouge">method</code> 来获取一个 <code class="language-plaintext highlighter-rouge">ServiceMethod</code> 对象。</p>

<h5 id="servicemethod">ServiceMethod</h5>

<p>打开 <code class="language-plaintext highlighter-rouge">ServiceMethod</code> 发现它是一个抽象类，有一个静态方法 <code class="language-plaintext highlighter-rouge">parseAnnotations</code> 和一个抽象方法 <code class="language-plaintext highlighter-rouge">invoke</code>。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">ServiceMethod</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
  <span class="kd">static</span> <span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="nc">ServiceMethod</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="nf">parseAnnotations</span><span class="o">(</span><span class="nc">Retrofit</span> <span class="n">retrofit</span><span class="o">,</span> <span class="nc">Method</span> <span class="n">method</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//对注解进行解析</span>
    <span class="nc">RequestFactory</span> <span class="n">requestFactory</span> <span class="o">=</span> <span class="nc">RequestFactory</span><span class="o">.</span><span class="na">parseAnnotations</span><span class="o">(</span><span class="n">retrofit</span><span class="o">,</span> <span class="n">method</span><span class="o">);</span>
	<span class="c1">//获取方法的返回类型</span>
    <span class="nc">Type</span> <span class="n">returnType</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="na">getGenericReturnType</span><span class="o">();</span>
    <span class="c1">//对返回类型进行校验</span>
    <span class="k">if</span> <span class="o">(</span><span class="nc">Utils</span><span class="o">.</span><span class="na">hasUnresolvableType</span><span class="o">(</span><span class="n">returnType</span><span class="o">))</span> <span class="o">{</span>
      <span class="k">throw</span> <span class="nf">methodError</span><span class="o">(</span><span class="n">method</span><span class="o">,</span>
          <span class="s">"Method return type must not include a type variable or wildcard: %s"</span><span class="o">,</span> <span class="n">returnType</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">returnType</span> <span class="o">==</span> <span class="kt">void</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">throw</span> <span class="nf">methodError</span><span class="o">(</span><span class="n">method</span><span class="o">,</span> <span class="s">"Service methods cannot return void."</span><span class="o">);</span>
    <span class="o">}</span>
	<span class="c1">//最终使用到HttpServiceMethod类</span>
    <span class="k">return</span> <span class="nc">HttpServiceMethod</span><span class="o">.</span><span class="na">parseAnnotations</span><span class="o">(</span><span class="n">retrofit</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">requestFactory</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="kd">abstract</span> <span class="no">T</span> <span class="nf">invoke</span><span class="o">(</span><span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">parseAnnotations</code> 方法就是对程序猿定义的接口中使用的注解进行解析。</p>

<p>最后是使用了<code class="language-plaintext highlighter-rouge">HttpServiceMethod.parseAnnotations</code>方法</p>

<h5 id="httpservicemethod">HttpServiceMethod</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/** Adapts an invocation of an interface method into an HTTP call. */</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="nc">HttpServiceMethod</span><span class="o">&lt;</span><span class="nc">ResponseT</span><span class="o">,</span> <span class="nc">ReturnT</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="nc">ServiceMethod</span><span class="o">&lt;</span><span class="nc">ReturnT</span><span class="o">&gt;</span> <span class="o">{</span>
  <span class="kd">static</span> <span class="o">&lt;</span><span class="nc">ResponseT</span><span class="o">,</span> <span class="nc">ReturnT</span><span class="o">&gt;</span> <span class="nc">HttpServiceMethod</span><span class="o">&lt;</span><span class="nc">ResponseT</span><span class="o">,</span> <span class="nc">ReturnT</span><span class="o">&gt;</span> <span class="nf">parseAnnotations</span><span class="o">(</span>
      <span class="nc">Retrofit</span> <span class="n">retrofit</span><span class="o">,</span> <span class="nc">Method</span> <span class="n">method</span><span class="o">,</span> <span class="nc">RequestFactory</span> <span class="n">requestFactory</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">CallAdapter</span><span class="o">&lt;</span><span class="nc">ResponseT</span><span class="o">,</span> <span class="nc">ReturnT</span><span class="o">&gt;</span> <span class="n">callAdapter</span> <span class="o">=</span> <span class="n">createCallAdapter</span><span class="o">(</span><span class="n">retrofit</span><span class="o">,</span> <span class="n">method</span><span class="o">);</span>
    <span class="c1">//...省略部分代码</span>
    <span class="nc">Converter</span><span class="o">&lt;</span><span class="nc">ResponseBody</span><span class="o">,</span> <span class="nc">ResponseT</span><span class="o">&gt;</span> <span class="n">responseConverter</span> <span class="o">=</span>
        <span class="n">createResponseConverter</span><span class="o">(</span><span class="n">retrofit</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span> <span class="n">responseType</span><span class="o">);</span>

    <span class="n">okhttp3</span><span class="o">.</span><span class="na">Call</span><span class="o">.</span><span class="na">Factory</span> <span class="n">callFactory</span> <span class="o">=</span> <span class="n">retrofit</span><span class="o">.</span><span class="na">callFactory</span><span class="o">;</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nc">HttpServiceMethod</span><span class="o">&lt;&gt;(</span><span class="n">requestFactory</span><span class="o">,</span> <span class="n">callFactory</span><span class="o">,</span> <span class="n">callAdapter</span><span class="o">,</span> <span class="n">responseConverter</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="c1">//...省略部分代码</span>

  <span class="nd">@Override</span> <span class="nc">ReturnT</span> <span class="nf">invoke</span><span class="o">(</span><span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">callAdapter</span><span class="o">.</span><span class="na">adapt</span><span class="o">(</span>
        <span class="k">new</span> <span class="nc">OkHttpCall</span><span class="o">&lt;&gt;(</span><span class="n">requestFactory</span><span class="o">,</span> <span class="n">args</span><span class="o">,</span> <span class="n">callFactory</span><span class="o">,</span> <span class="n">responseConverter</span><span class="o">));</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">HttpServiceMethod</code> 是 <code class="language-plaintext highlighter-rouge">ServiceMethod</code> 的子类。而在<code class="language-plaintext highlighter-rouge">parseAnnotations</code> 方法中构造了<code class="language-plaintext highlighter-rouge">HttpServiceMethod</code>实例并返回。</p>

<p><strong>因此，<code class="language-plaintext highlighter-rouge">loadServiceMethod</code>方法返回的是<code class="language-plaintext highlighter-rouge">HttpServiceMehod</code>对象</strong></p>

<p>这样下面代码的执行实际上是执行了 <code class="language-plaintext highlighter-rouge">HttpServiceMehod</code> 的 <code class="language-plaintext highlighter-rouge">invoke</code> 方法。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loadServiceMethod</span><span class="o">(</span><span class="n">method</span><span class="o">).</span><span class="na">invoke</span><span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">args</span> <span class="o">:</span> <span class="n">emptyArgs</span><span class="o">);</span>
</code></pre></div></div>

<p>再次翻看上文中<code class="language-plaintext highlighter-rouge">HttpServiceMethod</code>类</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="nc">ReturnT</span> <span class="nf">invoke</span><span class="o">(</span><span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">callAdapter</span><span class="o">.</span><span class="na">adapt</span><span class="o">(</span>
        <span class="k">new</span> <span class="nc">OkHttpCall</span><span class="o">&lt;&gt;(</span><span class="n">requestFactory</span><span class="o">,</span> <span class="n">args</span><span class="o">,</span> <span class="n">callFactory</span><span class="o">,</span> <span class="n">responseConverter</span><span class="o">));</span>
  <span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">invoke</code> 方法里有执行了<code class="language-plaintext highlighter-rouge">callAdapter.adapt</code>方法，参数为<code class="language-plaintext highlighter-rouge">OkHttpCall</code>，这个类实际上就是对<code class="language-plaintext highlighter-rouge">okhttp</code>网络请求的封装，这里也可以看出<strong><code class="language-plaintext highlighter-rouge">retrofit</code>内部是使用了<code class="language-plaintext highlighter-rouge">okhttp</code>来执行网络请求的</strong></p>

<h5 id="calladapter">CallAdapter</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">CallAdapter</span><span class="o">&lt;</span><span class="no">R</span><span class="o">,</span> <span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
  <span class="c1">//..省略部分代码</span>
  <span class="no">T</span> <span class="nf">adapt</span><span class="o">(</span><span class="nc">Call</span><span class="o">&lt;</span><span class="no">R</span><span class="o">&gt;</span> <span class="n">call</span><span class="o">);</span>
  <span class="c1">//CallAdapter抽象工厂类</span>
  <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">Factory</span> <span class="o">{</span>
    <span class="c1">//返回CallAdapter实例</span>
    <span class="kd">public</span> <span class="kd">abstract</span> <span class="nd">@Nullable</span> <span class="nc">CallAdapter</span><span class="o">&lt;?,</span> <span class="o">?&gt;</span> <span class="n">get</span><span class="o">(</span><span class="nc">Type</span> <span class="n">returnType</span><span class="o">,</span> <span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
        <span class="nc">Retrofit</span> <span class="n">retrofit</span><span class="o">);</span>

    <span class="c1">//..省略部分代码</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这是一个接口，内部有一个<code class="language-plaintext highlighter-rouge">Factory</code>抽象工厂类，用于获取<code class="language-plaintext highlighter-rouge">CallAdapter</code>对象。</p>

<p><code class="language-plaintext highlighter-rouge">CallAdapter</code> 有很多子类，那 <code class="language-plaintext highlighter-rouge">callAdapter.adapt</code> 方法执行的是哪个具体类的方法呢？实际上，<strong>从调试代码中可以发现是调用<code class="language-plaintext highlighter-rouge">DefaultCallFactory</code>中的内部实现类</strong></p>

<p><img src="../images/calladapter-adapt.png" alt="" /></p>

<h5 id="defaultcallaapterfactory">DefaultCallAapterFactory</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="nc">DefaultCallAdapterFactory</span> <span class="kd">extends</span> <span class="nc">CallAdapter</span><span class="o">.</span><span class="na">Factory</span> <span class="o">{</span>
  <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CallAdapter</span><span class="o">.</span><span class="na">Factory</span> <span class="no">INSTANCE</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DefaultCallAdapterFactory</span><span class="o">();</span>

  <span class="nd">@Override</span> <span class="kd">public</span> <span class="nd">@Nullable</span> <span class="nc">CallAdapter</span><span class="o">&lt;?,</span> <span class="o">?&gt;</span> <span class="n">get</span><span class="o">(</span>
      <span class="nc">Type</span> <span class="n">returnType</span><span class="o">,</span> <span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span> <span class="nc">Retrofit</span> <span class="n">retrofit</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">getRawType</span><span class="o">(</span><span class="n">returnType</span><span class="o">)</span> <span class="o">!=</span> <span class="nc">Call</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">final</span> <span class="nc">Type</span> <span class="n">responseType</span> <span class="o">=</span> <span class="nc">Utils</span><span class="o">.</span><span class="na">getCallResponseType</span><span class="o">(</span><span class="n">returnType</span><span class="o">);</span>
    <span class="c1">//返回一个CallAapter实例</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nc">CallAdapter</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">,</span> <span class="nc">Call</span><span class="o">&lt;?&gt;&gt;()</span> <span class="o">{</span>
      <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Type</span> <span class="nf">responseType</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">responseType</span><span class="o">;</span>
      <span class="o">}</span>

      <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Call</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">&gt;</span> <span class="nf">adapt</span><span class="o">(</span><span class="nc">Call</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">&gt;</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">//将参数返回，而这个参数就是OKHttpCall的实例</span>
        <span class="k">return</span> <span class="n">call</span><span class="o">;</span>
      <span class="o">}</span>
    <span class="o">};</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以发现，在<code class="language-plaintext highlighter-rouge">adapt</code>方法中就是将参数<code class="language-plaintext highlighter-rouge">call</code>返回。</p>

<p>所以下面代码返回的是<code class="language-plaintext highlighter-rouge">OkHttpCall</code>对象。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loadServiceMethod</span><span class="o">(</span><span class="n">method</span><span class="o">).</span><span class="na">invoke</span><span class="o">(</span><span class="n">args</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">args</span> <span class="o">:</span> <span class="n">emptyArgs</span><span class="o">);</span>
</code></pre></div></div>

<p>综上</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//创建了Github接口的代理类</span>
<span class="nc">GitHub</span> <span class="n">github</span> <span class="o">=</span> <span class="n">retrofit</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nc">GitHub</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="c1">//执行接口的方法，其实就是调用了代理类的方法，并最终返回了一个OKhttpCall对象</span>
<span class="c1">//而这个对象就是对Okhttp的封装</span>
<span class="nc">Call</span><span class="o">&lt;</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;&gt;</span> <span class="n">call</span> <span class="o">=</span> <span class="n">github</span><span class="o">.</span><span class="na">contributors</span><span class="o">(</span><span class="s">"square"</span><span class="o">,</span> <span class="s">"retrofit"</span><span class="o">);</span>
</code></pre></div></div>

<h4 id="0x02-执行结果">0x02 执行结果</h4>

<p>上文中获取到<code class="language-plaintext highlighter-rouge">OKhttpCall</code>对象，它只是把接口请求过程进行了封装，并没有真正的获取到接口数据。要获取到接口数据还需要调用<code class="language-plaintext highlighter-rouge">OkHttpCall.execute</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Contributor</span><span class="o">&gt;</span> <span class="n">contributors</span> <span class="o">=</span> <span class="n">call</span><span class="o">.</span><span class="na">execute</span><span class="o">().</span><span class="na">body</span><span class="o">();</span>
</code></pre></div></div>

<h5 id="callexecute-或-callenqueue">Call.execute 或 Call.enqueue</h5>

<p>这里的请求过程与前文中《源码分析OKHttp执行过程》介绍的是类似的。接一下</p>

<p>打开<code class="language-plaintext highlighter-rouge">OkHttpCall.execute</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Response</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="nf">execute</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="n">okhttp3</span><span class="o">.</span><span class="na">Call</span> <span class="n">call</span><span class="o">;</span>

    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">executed</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalStateException</span><span class="o">(</span><span class="s">"Already executed."</span><span class="o">);</span>
      <span class="n">executed</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>

      <span class="k">if</span> <span class="o">(</span><span class="n">creationFailure</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">creationFailure</span> <span class="k">instanceof</span> <span class="nc">IOException</span><span class="o">)</span> <span class="o">{</span>
          <span class="k">throw</span> <span class="o">(</span><span class="nc">IOException</span><span class="o">)</span> <span class="n">creationFailure</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">creationFailure</span> <span class="k">instanceof</span> <span class="nc">RuntimeException</span><span class="o">)</span> <span class="o">{</span>
          <span class="k">throw</span> <span class="o">(</span><span class="nc">RuntimeException</span><span class="o">)</span> <span class="n">creationFailure</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
          <span class="k">throw</span> <span class="o">(</span><span class="nc">Error</span><span class="o">)</span> <span class="n">creationFailure</span><span class="o">;</span>
        <span class="o">}</span>
      <span class="o">}</span>

      <span class="n">call</span> <span class="o">=</span> <span class="n">rawCall</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">call</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
          <span class="n">call</span> <span class="o">=</span> <span class="n">rawCall</span> <span class="o">=</span> <span class="n">createRawCall</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="o">|</span> <span class="nc">RuntimeException</span> <span class="o">|</span> <span class="nc">Error</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">throwIfFatal</span><span class="o">(</span><span class="n">e</span><span class="o">);</span> <span class="c1">//  Do not assign a fatal error to creationFailure.</span>
          <span class="n">creationFailure</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
          <span class="k">throw</span> <span class="n">e</span><span class="o">;</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">canceled</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">call</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="nf">parseResponse</span><span class="o">(</span><span class="n">call</span><span class="o">.</span><span class="na">execute</span><span class="o">());</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>这里的执行逻辑也很简单</p>

<ul>
  <li>使用<code class="language-plaintext highlighter-rouge">synchronized</code>进行同步操作</li>
  <li>进行异常处理</li>
  <li>调用<code class="language-plaintext highlighter-rouge">createRawCall</code> 创建 <code class="language-plaintext highlighter-rouge">okhttp3.Call</code> 对象</li>
  <li>执行 <code class="language-plaintext highlighter-rouge">okhttp</code> 的<code class="language-plaintext highlighter-rouge">Call.execute</code>方法，并解析<code class="language-plaintext highlighter-rouge">response</code>后返回请求结果</li>
</ul>

<p>同样地，异步请求操作也是类似的</p>

<p>打开<code class="language-plaintext highlighter-rouge">OkHttpCall.enqueue</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">enqueue</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Callback</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">callback</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">checkNotNull</span><span class="o">(</span><span class="n">callback</span><span class="o">,</span> <span class="s">"callback == null"</span><span class="o">);</span>

    <span class="n">okhttp3</span><span class="o">.</span><span class="na">Call</span> <span class="n">call</span><span class="o">;</span>
    <span class="nc">Throwable</span> <span class="n">failure</span><span class="o">;</span>

    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">executed</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalStateException</span><span class="o">(</span><span class="s">"Already executed."</span><span class="o">);</span>
      <span class="n">executed</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>

      <span class="n">call</span> <span class="o">=</span> <span class="n">rawCall</span><span class="o">;</span>
      <span class="n">failure</span> <span class="o">=</span> <span class="n">creationFailure</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">call</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">failure</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
          <span class="c1">//创建okhttp网络请求</span>
          <span class="n">call</span> <span class="o">=</span> <span class="n">rawCall</span> <span class="o">=</span> <span class="n">createRawCall</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Throwable</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">throwIfFatal</span><span class="o">(</span><span class="n">t</span><span class="o">);</span>
          <span class="n">failure</span> <span class="o">=</span> <span class="n">creationFailure</span> <span class="o">=</span> <span class="n">t</span><span class="o">;</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">failure</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">callback</span><span class="o">.</span><span class="na">onFailure</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">failure</span><span class="o">);</span>
      <span class="k">return</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">canceled</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">call</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
    <span class="o">}</span>
	<span class="c1">//最终是执行了OkHttp中的call.enqueue方法</span>
    <span class="c1">//并回调相应的接口</span>
    <span class="n">call</span><span class="o">.</span><span class="na">enqueue</span><span class="o">(</span><span class="k">new</span> <span class="n">okhttp3</span><span class="o">.</span><span class="na">Callback</span><span class="o">()</span> <span class="o">{</span>
      <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onResponse</span><span class="o">(</span><span class="n">okhttp3</span><span class="o">.</span><span class="na">Call</span> <span class="n">call</span><span class="o">,</span> <span class="n">okhttp3</span><span class="o">.</span><span class="na">Response</span> <span class="n">rawResponse</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">Response</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">response</span><span class="o">;</span>
        <span class="k">try</span> <span class="o">{</span>
          <span class="n">response</span> <span class="o">=</span> <span class="n">parseResponse</span><span class="o">(</span><span class="n">rawResponse</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Throwable</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">throwIfFatal</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
          <span class="n">callFailure</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
          <span class="k">return</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="k">try</span> <span class="o">{</span>
          <span class="n">callback</span><span class="o">.</span><span class="na">onResponse</span><span class="o">(</span><span class="nc">OkHttpCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Throwable</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">t</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
      <span class="o">}</span>

      <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onFailure</span><span class="o">(</span><span class="n">okhttp3</span><span class="o">.</span><span class="na">Call</span> <span class="n">call</span><span class="o">,</span> <span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">callFailure</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
      <span class="o">}</span>

      <span class="kd">private</span> <span class="kt">void</span> <span class="nf">callFailure</span><span class="o">(</span><span class="nc">Throwable</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
          <span class="n">callback</span><span class="o">.</span><span class="na">onFailure</span><span class="o">(</span><span class="nc">OkHttpCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Throwable</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">t</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">});</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>这个方法其实最终都是执行了<code class="language-plaintext highlighter-rouge">okhttp</code>的相应方法。</p>

<h4 id="0x03-总结">0x03 总结</h4>

<p><code class="language-plaintext highlighter-rouge">Retrofit</code> 其实一种更加高级的网络应用框架，通过代理模式简化了接口的定义，无需提供接口的具体实现就可以完成网络接口请求的执行。它的底层实际上是封装了 <code class="language-plaintext highlighter-rouge">okhttp</code> 的执行过程，也把对网络的操作进行了封装，而对于程序猿来说只需要关注业务逻辑，对网络请求的具体实现不必关心。</p>

<p>例如在本文开头的实例中我们只需要定义接口，定义实体类，其他工作都交给了 <code class="language-plaintext highlighter-rouge">Retrofit</code> ，接下来就是<code class="language-plaintext highlighter-rouge">Magic</code>。</p>]]></content><author><name>hylinux</name></author><category term="开源库" /><summary type="html"><![CDATA[Retrofit 是 square 公司的另一款广泛流行的网络请求框架。]]></summary></entry><entry><title type="html">源码分析OKHttp的执行过程</title><link href="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90OKHttp%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B" rel="alternate" type="text/html" title="源码分析OKHttp的执行过程" /><published>2018-10-27T00:00:00+00:00</published><updated>2018-10-27T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90OKHttp%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B</id><content type="html" xml:base="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90OKHttp%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><![CDATA[<p><code class="language-plaintext highlighter-rouge">OKHttp</code> 是目前 <code class="language-plaintext highlighter-rouge">Android</code> 平台主流的网络请求的基础框架。因此我们有必要对其源码进行阅读学习，了解其内部的原理、项目结构、以及请求的执行过程。</p>

<p>它的项目地址为：https://github.com/square/okhttp</p>

<h4 id="0x00-简单使用">0x00 简单使用</h4>

<p>先从一个简单的官方示例来看，这是一个同步 <code class="language-plaintext highlighter-rouge">GET</code> 请求</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">GetExample</span> <span class="o">{</span>
  <span class="c1">//1.http客户端</span>
  <span class="nc">OkHttpClient</span> <span class="n">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OkHttpClient</span><span class="o">();</span>
    
  <span class="nc">String</span> <span class="nf">run</span><span class="o">(</span><span class="nc">String</span> <span class="n">url</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="c1">//2.构造请求</span>
    <span class="nc">Request</span> <span class="n">request</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Request</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
        <span class="o">.</span><span class="na">url</span><span class="o">(</span><span class="n">url</span><span class="o">)</span>
        <span class="o">.</span><span class="na">build</span><span class="o">();</span>
	<span class="c1">//3.执行请求，获取响应数据</span>
    <span class="k">try</span> <span class="o">(</span><span class="nc">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">newCall</span><span class="o">(</span><span class="n">request</span><span class="o">).</span><span class="na">execute</span><span class="o">())</span> <span class="o">{</span>
      <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="na">body</span><span class="o">().</span><span class="na">string</span><span class="o">();</span>
    <span class="o">}</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="nc">GetExample</span> <span class="n">example</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GetExample</span><span class="o">();</span>
    <span class="nc">String</span> <span class="n">response</span> <span class="o">=</span> <span class="n">example</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="s">"https://raw.github.com/square/okhttp/master/README.md"</span><span class="o">);</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">response</span><span class="o">);</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以看出这个 <code class="language-plaintext highlighter-rouge">GET</code> 请求操作是很简单的。有几个很重要的接口</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">OKHttpClient</code> ： 它代表着 <code class="language-plaintext highlighter-rouge">http</code> 客户端</li>
  <li><code class="language-plaintext highlighter-rouge">Request</code>：它封装了请求对象，可以构造一个 <code class="language-plaintext highlighter-rouge">http</code> 请求对象</li>
  <li><code class="language-plaintext highlighter-rouge">Response</code>：封装了响应结果</li>
  <li><code class="language-plaintext highlighter-rouge">Call</code> ：<code class="language-plaintext highlighter-rouge">client.newCall</code>调用后生成一个请求执行对象<code class="language-plaintext highlighter-rouge">Call</code>，它封装了请求执行过程。</li>
</ul>

<p>这几个接口是程序员在使用 <code class="language-plaintext highlighter-rouge">OKHttp</code> 库中经常遇到的。</p>

<p>接下来将从这个示例开始阅读 <code class="language-plaintext highlighter-rouge">OkHttp</code> 的源码</p>

<h4 id="0x01-callexecute">0x01 Call.execute()</h4>

<p>跟进源码后发现这个方法是在 <code class="language-plaintext highlighter-rouge">Call</code> 中的接口</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * A call is a request that has been prepared for execution. A call can be canceled. As this object
 * represents a single request/response pair (stream), it cannot be executed twice.
 */</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Call</span> <span class="kd">extends</span> <span class="nc">Cloneable</span> <span class="o">{</span>
  <span class="c1">//...</span>
  <span class="c1">//同步执行请求</span>
  <span class="nc">Response</span> <span class="nf">execute</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">;</span>

  <span class="c1">//将请求加入队列</span>
  <span class="kt">void</span> <span class="nf">enqueue</span><span class="o">(</span><span class="nc">Callback</span> <span class="n">responseCallback</span><span class="o">);</span>

  <span class="c1">//...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>从源码注释知道，<code class="language-plaintext highlighter-rouge">Call</code> 是一个准备请求的执行对象，它可以被取消，代表一个 “请求/响应” 对，不能执行两次。</p>

<h5 id="realcall">RealCall</h5>

<p><code class="language-plaintext highlighter-rouge">Call</code> 的实现类是 <code class="language-plaintext highlighter-rouge">RealCall</code>，因此 <code class="language-plaintext highlighter-rouge">execute</code> 方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Response</span> <span class="nf">execute</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">executed</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalStateException</span><span class="o">(</span><span class="s">"Already Executed"</span><span class="o">);</span>
      <span class="n">executed</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="n">captureCallStackTrace</span><span class="o">();</span>
    <span class="n">eventListener</span><span class="o">.</span><span class="na">callStart</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
    <span class="k">try</span> <span class="o">{</span>
      <span class="n">client</span><span class="o">.</span><span class="na">dispatcher</span><span class="o">().</span><span class="na">executed</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
      <span class="nc">Response</span> <span class="n">result</span> <span class="o">=</span> <span class="n">getResponseWithInterceptorChain</span><span class="o">();</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IOException</span><span class="o">(</span><span class="s">"Canceled"</span><span class="o">);</span>
      <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">eventListener</span><span class="o">.</span><span class="na">callFailed</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
      <span class="k">throw</span> <span class="n">e</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
      <span class="n">client</span><span class="o">.</span><span class="na">dispatcher</span><span class="o">().</span><span class="na">finished</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
    <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>这个方法也不是很长，逻辑很简单：</p>

<ul>
  <li>同步锁检查该请求是否已经执行，如果没有则标记<code class="language-plaintext highlighter-rouge">executed = ture</code>，否则抛出异常</li>
  <li>调用了回调函数<code class="language-plaintext highlighter-rouge">callStart</code></li>
  <li><code class="language-plaintext highlighter-rouge">okhttp</code>客户端调用<code class="language-plaintext highlighter-rouge">dispatcher</code> 将执行请求对象</li>
  <li>调用了<code class="language-plaintext highlighter-rouge">getResponseWithInterceptorChain</code> 方法获取到响应数据<code class="language-plaintext highlighter-rouge">Response</code>，这个方法很重要，后面会继续跟进</li>
  <li>然后是对请求失败的回调<code class="language-plaintext highlighter-rouge">callFailed</code></li>
  <li>最后还是使用<code class="language-plaintext highlighter-rouge">dispather</code>对象调用<code class="language-plaintext highlighter-rouge">finished</code>方法，完成请求</li>
</ul>

<p>这里的逻辑还是比较清晰的，出现两个重要的方法</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">dispatcher.execute</code></li>
  <li><code class="language-plaintext highlighter-rouge">getResponseWithInterceptorChain</code></li>
</ol>

<p>接下来分别看这两个方法</p>

<h4 id="0x02-dispatcher">0x02 Dispatcher</h4>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">Dispatcher</span> <span class="o">{</span>
 	
  <span class="cm">/** Executes calls. Created lazily. */</span>
  <span class="kd">private</span> <span class="nd">@Nullable</span> <span class="nc">ExecutorService</span> <span class="n">executorService</span><span class="o">;</span>

  <span class="cm">/** Ready async calls in the order they'll be run. */</span>
  <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Deque</span><span class="o">&lt;</span><span class="nc">AsyncCall</span><span class="o">&gt;</span> <span class="n">readyAsyncCalls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayDeque</span><span class="o">&lt;&gt;();</span>

  <span class="cm">/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */</span>
  <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Deque</span><span class="o">&lt;</span><span class="nc">AsyncCall</span><span class="o">&gt;</span> <span class="n">runningAsyncCalls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayDeque</span><span class="o">&lt;&gt;();</span>

  <span class="cm">/** Running synchronous calls. Includes canceled calls that haven't finished yet. */</span>
  <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Deque</span><span class="o">&lt;</span><span class="nc">RealCall</span><span class="o">&gt;</span> <span class="n">runningSyncCalls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayDeque</span><span class="o">&lt;&gt;();</span>
  <span class="c1">//...</span>

  <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">enqueue</span><span class="o">(</span><span class="nc">AsyncCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">runningAsyncCalls</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">&lt;</span> <span class="n">maxRequests</span> <span class="o">&amp;&amp;</span> <span class="n">runningCallsForHost</span><span class="o">(</span><span class="n">call</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">maxRequestsPerHost</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">runningAsyncCalls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
      <span class="n">executorService</span><span class="o">().</span><span class="na">execute</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
      <span class="n">readyAsyncCalls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
    <span class="o">}</span>
  <span class="o">}</span>

  <span class="cm">/** Used by {@code Call#execute} to signal it is in-flight. */</span>
  <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">executed</span><span class="o">(</span><span class="nc">RealCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">runningSyncCalls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="cm">/** Used by {@code AsyncCall#run} to signal completion. */</span>
  <span class="kt">void</span> <span class="nf">finished</span><span class="o">(</span><span class="nc">AsyncCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">finished</span><span class="o">(</span><span class="n">runningAsyncCalls</span><span class="o">,</span> <span class="n">call</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="cm">/** Used by {@code Call#execute} to signal completion. */</span>
  <span class="kt">void</span> <span class="nf">finished</span><span class="o">(</span><span class="nc">RealCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">finished</span><span class="o">(</span><span class="n">runningSyncCalls</span><span class="o">,</span> <span class="n">call</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="kd">private</span> <span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="nf">finished</span><span class="o">(</span><span class="nc">Deque</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">calls</span><span class="o">,</span> <span class="no">T</span> <span class="n">call</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">promoteCalls</span><span class="o">)</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">runningCallsCount</span><span class="o">;</span>
    <span class="nc">Runnable</span> <span class="n">idleCallback</span><span class="o">;</span>
    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(!</span><span class="n">calls</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">call</span><span class="o">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">AssertionError</span><span class="o">(</span><span class="s">"Call wasn't in-flight!"</span><span class="o">);</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">promoteCalls</span><span class="o">)</span> <span class="n">promoteCalls</span><span class="o">();</span>
      <span class="n">runningCallsCount</span> <span class="o">=</span> <span class="n">runningCallsCount</span><span class="o">();</span>
      <span class="n">idleCallback</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">idleCallback</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">runningCallsCount</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">idleCallback</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">idleCallback</span><span class="o">.</span><span class="na">run</span><span class="o">();</span>
    <span class="o">}</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以看出<code class="language-plaintext highlighter-rouge">Dispatcher</code> 是一个调度器，它内部有一个线程池<code class="language-plaintext highlighter-rouge">executorService</code> ,还有三个队列，分别代表同步请求进行队列、异步请求等待队列、异步请求执行队列。</p>

<p>我们发现<strong>调用<code class="language-plaintext highlighter-rouge">execute</code>方法时就是将<code class="language-plaintext highlighter-rouge">Call</code>对象加入到同步请求进行队列<code class="language-plaintext highlighter-rouge">runningSyncCalls</code>中，而调用<code class="language-plaintext highlighter-rouge">finished</code> 方法则是将<code class="language-plaintext highlighter-rouge">Call</code>请求从队列中移除</strong></p>

<h4 id="0x03-getresponsewithinterceptorchain">0x03 getResponseWithInterceptorChain</h4>

<p>现在在回到<code class="language-plaintext highlighter-rouge">RealCall</code> 源码中，这个方法可以说是<code class="language-plaintext highlighter-rouge">OkHttp</code>最关键的部分了</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Response</span> <span class="nf">getResponseWithInterceptorChain</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="c1">// Build a full stack of interceptors.</span>
    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Interceptor</span><span class="o">&gt;</span> <span class="n">interceptors</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="n">client</span><span class="o">.</span><span class="na">interceptors</span><span class="o">());</span><span class="c1">//添加程序员自定义的的拦截器</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">retryAndFollowUpInterceptor</span><span class="o">);</span><span class="c1">//重试和重定向拦截器</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">BridgeInterceptor</span><span class="o">(</span><span class="n">client</span><span class="o">.</span><span class="na">cookieJar</span><span class="o">()));</span><span class="c1">//处理cookie的拦截器</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">CacheInterceptor</span><span class="o">(</span><span class="n">client</span><span class="o">.</span><span class="na">internalCache</span><span class="o">()));</span><span class="c1">//处理缓存的拦截器</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">ConnectInterceptor</span><span class="o">(</span><span class="n">client</span><span class="o">));</span><span class="c1">//负责连接的拦截器</span>
    <span class="k">if</span> <span class="o">(!</span><span class="n">forWebSocket</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">interceptors</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="n">client</span><span class="o">.</span><span class="na">networkInterceptors</span><span class="o">());</span><span class="c1">//添加程序员自定义的network拦截器</span>
    <span class="o">}</span>
    <span class="n">interceptors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">CallServerInterceptor</span><span class="o">(</span><span class="n">forWebSocket</span><span class="o">));</span><span class="c1">//调用服务拦截器</span>

    <span class="nc">Interceptor</span><span class="o">.</span><span class="na">Chain</span> <span class="n">chain</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RealInterceptorChain</span><span class="o">(</span><span class="n">interceptors</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span>
        <span class="n">originalRequest</span><span class="o">,</span> <span class="k">this</span><span class="o">,</span> <span class="n">eventListener</span><span class="o">,</span> <span class="n">client</span><span class="o">.</span><span class="na">connectTimeoutMillis</span><span class="o">(),</span>
        <span class="n">client</span><span class="o">.</span><span class="na">readTimeoutMillis</span><span class="o">(),</span> <span class="n">client</span><span class="o">.</span><span class="na">writeTimeoutMillis</span><span class="o">());</span>

    <span class="k">return</span> <span class="n">chain</span><span class="o">.</span><span class="na">proceed</span><span class="o">(</span><span class="n">originalRequest</span><span class="o">);</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>在添加了一系列的拦截器之后，又构造了一个拦截器责任链，这个<code class="language-plaintext highlighter-rouge">RealInterceptorChain</code> 包含了所有的拦截器对象。然后调用<code class="language-plaintext highlighter-rouge">chain.proceed</code>方法开始执行请求，这时就到了<code class="language-plaintext highlighter-rouge">RealInterceptorChain</code> 这个类中。</p>

<h4 id="0x04-realinterceptorchain">0x04 RealInterceptorChain</h4>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Response</span> <span class="nf">proceed</span><span class="o">(</span><span class="nc">Request</span> <span class="n">request</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="k">return</span> <span class="nf">proceed</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">streamAllocation</span><span class="o">,</span> <span class="n">httpCodec</span><span class="o">,</span> <span class="n">connection</span><span class="o">);</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="nc">Response</span> <span class="nf">proceed</span><span class="o">(</span><span class="nc">Request</span> <span class="n">request</span><span class="o">,</span> <span class="nc">StreamAllocation</span> <span class="n">streamAllocation</span><span class="o">,</span> <span class="nc">HttpCodec</span> <span class="n">httpCodec</span><span class="o">,</span>
      <span class="nc">RealConnection</span> <span class="n">connection</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&gt;=</span> <span class="n">interceptors</span><span class="o">.</span><span class="na">size</span><span class="o">())</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">AssertionError</span><span class="o">();</span>

    <span class="n">calls</span><span class="o">++;</span>

    <span class="c1">//省略无关代码...</span>

    <span class="c1">//1. 执行拦截器责任链中的下一个拦截器</span>
    <span class="nc">RealInterceptorChain</span> <span class="n">next</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RealInterceptorChain</span><span class="o">(</span><span class="n">interceptors</span><span class="o">,</span> <span class="n">streamAllocation</span><span class="o">,</span> <span class="n">httpCodec</span><span class="o">,</span>
        <span class="n">connection</span><span class="o">,</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">request</span><span class="o">,</span> <span class="n">call</span><span class="o">,</span> <span class="n">eventListener</span><span class="o">,</span> <span class="n">connectTimeout</span><span class="o">,</span> <span class="n">readTimeout</span><span class="o">,</span>
        <span class="n">writeTimeout</span><span class="o">);</span>
    <span class="c1">//2. 获取当前的拦截器</span>
    <span class="nc">Interceptor</span> <span class="n">interceptor</span> <span class="o">=</span> <span class="n">interceptors</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
    <span class="c1">//3. 执行拦截，并返回响应</span>
    <span class="nc">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">interceptor</span><span class="o">.</span><span class="na">intercept</span><span class="o">(</span><span class="n">next</span><span class="o">);</span>

    <span class="c1">//省略...</span>

    <span class="k">return</span> <span class="n">response</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>可以看到，在<code class="language-plaintext highlighter-rouge">proceed</code>方法，又构造了<code class="language-plaintext highlighter-rouge">RealInterceptorChain</code>并且调用了<code class="language-plaintext highlighter-rouge">interceptor.intercept</code>方法，</p>

<p>而这个方法中又会调用<code class="language-plaintext highlighter-rouge">next.proceed</code>方法，直至返回<code class="language-plaintext highlighter-rouge">response</code>。这个过程有点像递归调用。</p>

<h4 id="0x05-interceptor">0x05 Interceptor</h4>

<p>拦截器，它是一个接口，内部还有一个<code class="language-plaintext highlighter-rouge">Chain</code>接口</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Interceptor</span> <span class="o">{</span>
  <span class="nc">Response</span> <span class="nf">intercept</span><span class="o">(</span><span class="nc">Chain</span> <span class="n">chain</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">;</span>

  <span class="kd">interface</span> <span class="nc">Chain</span> <span class="o">{</span>
    <span class="nc">Request</span> <span class="nf">request</span><span class="o">();</span>

    <span class="nc">Response</span> <span class="nf">proceed</span><span class="o">(</span><span class="nc">Request</span> <span class="n">request</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">;</span>

    <span class="cm">/**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */</span>
    <span class="nd">@Nullable</span> <span class="nc">Connection</span> <span class="nf">connection</span><span class="o">();</span>

    <span class="nc">Call</span> <span class="nf">call</span><span class="o">();</span>

    <span class="kt">int</span> <span class="nf">connectTimeoutMillis</span><span class="o">();</span>

    <span class="nc">Chain</span> <span class="nf">withConnectTimeout</span><span class="o">(</span><span class="kt">int</span> <span class="n">timeout</span><span class="o">,</span> <span class="nc">TimeUnit</span> <span class="n">unit</span><span class="o">);</span>

    <span class="kt">int</span> <span class="nf">readTimeoutMillis</span><span class="o">();</span>

    <span class="nc">Chain</span> <span class="nf">withReadTimeout</span><span class="o">(</span><span class="kt">int</span> <span class="n">timeout</span><span class="o">,</span> <span class="nc">TimeUnit</span> <span class="n">unit</span><span class="o">);</span>

    <span class="kt">int</span> <span class="nf">writeTimeoutMillis</span><span class="o">();</span>

    <span class="nc">Chain</span> <span class="nf">withWriteTimeout</span><span class="o">(</span><span class="kt">int</span> <span class="n">timeout</span><span class="o">,</span> <span class="nc">TimeUnit</span> <span class="n">unit</span><span class="o">);</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>所有的拦截器都需要实现这个接口。</p>

<h4 id="0x06-异步的情况">0x06 异步的情况</h4>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">AsynchronousGet</span> <span class="o">{</span>
  <span class="kd">private</span> <span class="kd">final</span> <span class="nc">OkHttpClient</span> <span class="n">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OkHttpClient</span><span class="o">();</span>

  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
    <span class="nc">Request</span> <span class="n">request</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Request</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span>
        <span class="o">.</span><span class="na">url</span><span class="o">(</span><span class="s">"http://publicobject.com/helloworld.txt"</span><span class="o">)</span>
        <span class="o">.</span><span class="na">build</span><span class="o">();</span>
	<span class="c1">//调用enqueue方法，并设置回调接口</span>
    <span class="n">client</span><span class="o">.</span><span class="na">newCall</span><span class="o">(</span><span class="n">request</span><span class="o">).</span><span class="na">enqueue</span><span class="o">(</span><span class="k">new</span> <span class="nc">Callback</span><span class="o">()</span> <span class="o">{</span>
      <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onFailure</span><span class="o">(</span><span class="nc">Call</span> <span class="n">call</span><span class="o">,</span> <span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
      <span class="o">}</span>

      <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onResponse</span><span class="o">(</span><span class="nc">Call</span> <span class="n">call</span><span class="o">,</span> <span class="nc">Response</span> <span class="n">response</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
       	<span class="c1">//这里获取到响应结果数据</span>
      <span class="o">}</span>
    <span class="o">});</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>然后我们再看<code class="language-plaintext highlighter-rouge">RealCall</code>中的<code class="language-plaintext highlighter-rouge">enqueue</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">enqueue</span><span class="o">(</span><span class="nc">Callback</span> <span class="n">responseCallback</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">executed</span><span class="o">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IllegalStateException</span><span class="o">(</span><span class="s">"Already Executed"</span><span class="o">);</span>
      <span class="n">executed</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="n">captureCallStackTrace</span><span class="o">();</span>
    <span class="n">eventListener</span><span class="o">.</span><span class="na">callStart</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
    <span class="c1">//最终执行了dispatcher的enqueue方法</span>
    <span class="n">client</span><span class="o">.</span><span class="na">dispatcher</span><span class="o">().</span><span class="na">enqueue</span><span class="o">(</span><span class="k">new</span> <span class="nc">AsyncCall</span><span class="o">(</span><span class="n">responseCallback</span><span class="o">));</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>其实是执行了<code class="language-plaintext highlighter-rouge">dispatcher</code>中的<code class="language-plaintext highlighter-rouge">enqueue</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">enqueue</span><span class="o">(</span><span class="nc">AsyncCall</span> <span class="n">call</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">runningAsyncCalls</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">&lt;</span> <span class="n">maxRequests</span> <span class="o">&amp;&amp;</span> <span class="n">runningCallsForHost</span><span class="o">(</span><span class="n">call</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">maxRequestsPerHost</span><span class="o">)</span> <span class="o">{</span>
      <span class="n">runningAsyncCalls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
      <span class="n">executorService</span><span class="o">().</span><span class="na">execute</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
      <span class="n">readyAsyncCalls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">call</span><span class="o">);</span>
    <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div></div>

<p>在<code class="language-plaintext highlighter-rouge">dispatcher</code>中通过线程池来执行<code class="language-plaintext highlighter-rouge">AsyncCall</code>对象，因此跟进到<code class="language-plaintext highlighter-rouge">AsyncCall</code>中的<code class="language-plaintext highlighter-rouge">execute</code>方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
      <span class="kt">boolean</span> <span class="n">signalledCallback</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
      <span class="k">try</span> <span class="o">{</span>
        <span class="c1">//最终还是调用了getResponseWithInterceptorChain()！！！</span>
        <span class="nc">Response</span> <span class="n">response</span> <span class="o">=</span> <span class="n">getResponseWithInterceptorChain</span><span class="o">();</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">retryAndFollowUpInterceptor</span><span class="o">.</span><span class="na">isCanceled</span><span class="o">())</span> <span class="o">{</span>
          <span class="n">signalledCallback</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
          <span class="n">responseCallback</span><span class="o">.</span><span class="na">onFailure</span><span class="o">(</span><span class="nc">RealCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="k">new</span> <span class="nc">IOException</span><span class="o">(</span><span class="s">"Canceled"</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
          <span class="n">signalledCallback</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
          <span class="n">responseCallback</span><span class="o">.</span><span class="na">onResponse</span><span class="o">(</span><span class="nc">RealCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">response</span><span class="o">);</span>
        <span class="o">}</span>
      <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">signalledCallback</span><span class="o">)</span> <span class="o">{</span>
          <span class="c1">// Do not signal the callback twice!</span>
          <span class="nc">Platform</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">log</span><span class="o">(</span><span class="no">INFO</span><span class="o">,</span> <span class="s">"Callback failure for "</span> <span class="o">+</span> <span class="n">toLoggableString</span><span class="o">(),</span> <span class="n">e</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
          <span class="n">eventListener</span><span class="o">.</span><span class="na">callFailed</span><span class="o">(</span><span class="nc">RealCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
          <span class="n">responseCallback</span><span class="o">.</span><span class="na">onFailure</span><span class="o">(</span><span class="nc">RealCall</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
        <span class="o">}</span>
      <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="n">client</span><span class="o">.</span><span class="na">dispatcher</span><span class="o">().</span><span class="na">finished</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
      <span class="o">}</span>
    <span class="o">}</span>
</code></pre></div></div>

<p><strong>发现最终还是执行了<code class="language-plaintext highlighter-rouge">getResponseWithInterceptorChain</code></strong>，因此不管是同步还是异步、最终的流程还是一样。</p>

<h4 id="0x07-总结">0x07 总结</h4>

<ol>
  <li><code class="language-plaintext highlighter-rouge">OKHttpClient</code></li>
</ol>

<p>这是一个 <code class="language-plaintext highlighter-rouge">http</code> 客户端。构建很简单，可以使用无参构造函数。其内部是通过 <code class="language-plaintext highlighter-rouge">Builder</code> 对象进行构建的。也可以通过其内部静态类 <code class="language-plaintext highlighter-rouge">Builder</code> 来构建，然后通过 <code class="language-plaintext highlighter-rouge">builder</code> 设置 <code class="language-plaintext highlighter-rouge">OkHttpClient</code> 构造参数。</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Request</code></li>
</ol>

<p>请求对象。其内部也是使用 <code class="language-plaintext highlighter-rouge">Builder</code> 模式封装了构造的过程，通过<code class="language-plaintext highlighter-rouge">Builder</code>使用链式调用也是目前很多开源库中常见的模式。</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Response</code></li>
</ol>

<p>响应结果。客户端执行后返回响应结果，通过 <code class="language-plaintext highlighter-rouge">Response</code> 可以很方便的获取到响应数据。</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Call</code></li>
</ol>

<p>请求执行。可以执行同步或者异步的请求，分别将请求发送到<code class="language-plaintext highlighter-rouge">dispatcher</code></p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Dispatcher</code></li>
</ol>

<p>调度器。其内部有一个线程池，并维护了三个队列：同步进行请求队列、异步请求等待队列、异步请求进行队列。</p>

<p>还有两个重要的方法<code class="language-plaintext highlighter-rouge">execute</code>和<code class="language-plaintext highlighter-rouge">enqueue</code>方法，分别代表同步、异步的方法。这两个方法的最终的执行流程都是一样的</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">Interceptor</code></li>
</ol>

<p>拦截器。拦截器在<code class="language-plaintext highlighter-rouge">OKHttpClient</code>中使是用责任链模式来实现的。<code class="language-plaintext highlighter-rouge">Okhttp</code> 中的关键的流程是通过拦截器责任链来完成的。</p>]]></content><author><name>hylinux</name></author><category term="开源库" /><summary type="html"><![CDATA[OKHttp 是目前 Android 平台主流的网络请求的基础框架。因此我们有必要对其源码进行阅读学习，了解其内部的原理、项目结构、以及请求的执行过程。]]></summary></entry><entry><title type="html">源码阅读之Java栈的实现</title><link href="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BJava%E6%A0%88%E7%9A%84%E5%AE%9E%E7%8E%B0" rel="alternate" type="text/html" title="源码阅读之Java栈的实现" /><published>2018-10-18T00:00:00+00:00</published><updated>2018-10-18T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BJava%E6%A0%88%E7%9A%84%E5%AE%9E%E7%8E%B0</id><content type="html" xml:base="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BJava%E6%A0%88%E7%9A%84%E5%AE%9E%E7%8E%B0"><![CDATA[<h4 id="0x00-栈">0x00 栈</h4>

<p>栈是 <code class="language-plaintext highlighter-rouge">Last-In-First-Out</code> （后进先出）的线性表。对栈的操作主要有两个：入栈(push)和出栈(pop)。因此它也是一种操作受限的线性表。尽管如此，它在计算机中应用非常广泛，是一种非常基础的数据结构。</p>

<h4 id="0x01-源码">0x01 源码</h4>

<p>从源码中可以看出栈也是一种非常简单的数据结构。栈的源码非常简洁，只有100多行代码。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Stack</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="nc">Vector</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="o">{</span>
 	
    <span class="kd">public</span> <span class="nf">Stack</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="c1">//入栈操作</span>
    <span class="kd">public</span> <span class="no">E</span> <span class="nf">push</span><span class="o">(</span><span class="no">E</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">addElement</span><span class="o">(</span><span class="n">item</span><span class="o">);</span>

        <span class="k">return</span> <span class="n">item</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">//出栈操作</span>
    <span class="kd">public</span> <span class="kd">synchronized</span> <span class="no">E</span> <span class="nf">pop</span><span class="o">()</span> <span class="o">{</span>
        <span class="no">E</span>       <span class="n">obj</span><span class="o">;</span>
        <span class="kt">int</span>     <span class="n">len</span> <span class="o">=</span> <span class="n">size</span><span class="o">();</span>

        <span class="n">obj</span> <span class="o">=</span> <span class="n">peek</span><span class="o">();</span>
        <span class="n">removeElementAt</span><span class="o">(</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>

        <span class="k">return</span> <span class="n">obj</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">//查看栈元素，不会删除</span>
    <span class="kd">public</span> <span class="kd">synchronized</span> <span class="no">E</span> <span class="nf">peek</span><span class="o">()</span> <span class="o">{</span>
        <span class="kt">int</span>     <span class="n">len</span> <span class="o">=</span> <span class="n">size</span><span class="o">();</span>

        <span class="k">if</span> <span class="o">(</span><span class="n">len</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">EmptyStackException</span><span class="o">();</span>
        <span class="k">return</span> <span class="nf">elementAt</span><span class="o">(</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">//检查是否为空栈</span>
    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">empty</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nf">size</span><span class="o">()</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">//查找元素，如果找到则返回从栈顶开始计数的位置，否则返回-1</span>
    <span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">search</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
        <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">lastIndexOf</span><span class="o">(</span><span class="n">o</span><span class="o">);</span>

        <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">return</span> <span class="nf">size</span><span class="o">()</span> <span class="o">-</span> <span class="n">i</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="cm">/** use serialVersionUID from JDK 1.0.2 for interoperability */</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="mi">1224463164541339165L</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>它继承于 <code class="language-plaintext highlighter-rouge">Vector</code> 类，这个跟前面讲的 <code class="language-plaintext highlighter-rouge">ArrayList</code> 是一样的，只不过 <code class="language-plaintext highlighter-rouge">Vector</code> 类方法是同步的，因此 <code class="language-plaintext highlighter-rouge">Stack</code> 也是线程安全的。</p>

<h5 id="push">push</h5>

<p>入栈</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">push</span><span class="o">(</span><span class="no">E</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">addElement</span><span class="o">(</span><span class="n">item</span><span class="o">);</span>

	<span class="k">return</span> <span class="n">item</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>虽然此方法没有声明 <code class="language-plaintext highlighter-rouge">synchronized</code>，但内部调用一个 <code class="language-plaintext highlighter-rouge">addElement</code> 来实现入栈操作。这个操作方法是在父类中实现的，它被声明为线程安全的，因此它也是线程安全的。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">addElement</span><span class="o">(</span><span class="no">E</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">modCount</span><span class="o">++;</span>
	<span class="n">ensureCapacityHelper</span><span class="o">(</span><span class="n">elementCount</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>
	<span class="n">elementData</span><span class="o">[</span><span class="n">elementCount</span><span class="o">++]</span> <span class="o">=</span> <span class="n">obj</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">ensureCapacityHelper</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// overflow-conscious code</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">minCapacity</span> <span class="o">-</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">grow</span><span class="o">(</span><span class="n">minCapacity</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">//扩容方法</span>
<span class="c1">//这里跟 ArrayList 扩容算法有点不一样，ArrayList 一次是扩容为原来的1.5倍，Vector 默认扩容为原来的1倍</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">grow</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// overflow-conscious code</span>
	<span class="kt">int</span> <span class="n">oldCapacity</span> <span class="o">=</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
	<span class="kt">int</span> <span class="n">newCapacity</span> <span class="o">=</span> <span class="n">oldCapacity</span> <span class="o">+</span> <span class="o">((</span><span class="n">capacityIncrement</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">?</span>
                                         <span class="n">capacityIncrement</span> <span class="o">:</span> <span class="n">oldCapacity</span><span class="o">);</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">newCapacity</span> <span class="o">-</span> <span class="n">minCapacity</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">newCapacity</span> <span class="o">=</span> <span class="n">minCapacity</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">newCapacity</span> <span class="o">-</span> <span class="no">MAX_ARRAY_SIZE</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">newCapacity</span> <span class="o">=</span> <span class="n">hugeCapacity</span><span class="o">(</span><span class="n">minCapacity</span><span class="o">);</span>
	<span class="n">elementData</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">newCapacity</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>虽然 <code class="language-plaintext highlighter-rouge">push</code> 方法没有声明 <code class="language-plaintext highlighter-rouge">synchronized</code> 但 <code class="language-plaintext highlighter-rouge">addElement</code> 方法中有。在这个方法里</p>

<ul>
  <li>首先把 <code class="language-plaintext highlighter-rouge">modCount</code> 加 1，记录一次对数据结构的操作。</li>
  <li>然后检查数组容量是否足够，不够则扩容。</li>
  <li>最后把元素对象添加到数组末尾。</li>
</ul>

<p>需要注意的是<strong><code class="language-plaintext highlighter-rouge">Vector</code>的扩容策略是默认一次扩容为原来的1倍，这与<code class="language-plaintext highlighter-rouge">ArrayList</code> 一次扩容原来的1.5倍不同</strong>。</p>

<h5 id="pop">pop</h5>

<p>出栈</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">synchronized</span> <span class="no">E</span> <span class="nf">pop</span><span class="o">()</span> <span class="o">{</span>
	<span class="no">E</span>       <span class="n">obj</span><span class="o">;</span>
	<span class="kt">int</span>     <span class="n">len</span> <span class="o">=</span> <span class="n">size</span><span class="o">();</span>

	<span class="n">obj</span> <span class="o">=</span> <span class="n">peek</span><span class="o">();</span>
	<span class="n">removeElementAt</span><span class="o">(</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>

	<span class="k">return</span> <span class="n">obj</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">pop</code> 方法被声明为 <code class="language-plaintext highlighter-rouge">synchronized</code> ，是线程安全的方法。它通过 <code class="language-plaintext highlighter-rouge">peek</code> 方法获取到栈顶元素对象，然后调用父类的 <code class="language-plaintext highlighter-rouge">removeElementAt</code> 方法把栈顶元素删除。</p>

<h5 id="peek">peek</h5>

<p>查看栈顶元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">synchronized</span> <span class="no">E</span> <span class="nf">peek</span><span class="o">()</span> <span class="o">{</span>
    <span class="kt">int</span>     <span class="n">len</span> <span class="o">=</span> <span class="n">size</span><span class="o">();</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">len</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">EmptyStackException</span><span class="o">();</span>
    <span class="k">return</span> <span class="nf">elementAt</span><span class="o">(</span><span class="n">len</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>

</code></pre></div></div>

<p>这个方法也是线程安全的。可以看出如果栈的大小为0时，执行 <code class="language-plaintext highlighter-rouge">peek</code> 方法会抛出 <code class="language-plaintext highlighter-rouge">EmptyStackException</code> 异常。</p>

<h5 id="search">search</h5>

<p>在栈中搜索元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">search</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">lastIndexOf</span><span class="o">(</span><span class="n">o</span><span class="o">);</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nf">size</span><span class="o">()</span> <span class="o">-</span> <span class="n">i</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>继续查看父类代码</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">lastIndexOf</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="nf">lastIndexOf</span><span class="o">(</span><span class="n">o</span><span class="o">,</span> <span class="n">elementCount</span><span class="o">-</span><span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">lastIndexOf</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">,</span> <span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&gt;=</span> <span class="n">elementCount</span><span class="o">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">index</span> <span class="o">+</span> <span class="s">" &gt;= "</span><span class="o">+</span> <span class="n">elementCount</span><span class="o">);</span>

    <span class="k">if</span> <span class="o">(</span><span class="n">o</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span><span class="c1">//如果查找的元素是空的则遍历找到最接近栈的空元素</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">index</span><span class="o">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">--)</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]==</span><span class="kc">null</span><span class="o">)</span>
                <span class="k">return</span> <span class="n">i</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">index</span><span class="o">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">--)</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">o</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span><span class="c1">//通过equals方法来判断两个元素是否相同</span>
                <span class="k">return</span> <span class="n">i</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在栈中查询一个元素需要遍历，时间复杂度为O(n)。</p>

<h4 id="0x02-总结">0x02 总结</h4>

<p>栈是一种<code class="language-plaintext highlighter-rouge">LIFO</code>的数据结构，它基于 <code class="language-plaintext highlighter-rouge">Vector</code> 来实现，所有的方法都是线程安全的。入栈和出栈操作的时间复杂度为O(1)，查找元素的时间复杂度为O(n)。</p>]]></content><author><name>hylinux</name></author><category term="数据结构" /><summary type="html"><![CDATA[栈是一种非常基础的数据结构，我们看看Java标准实现]]></summary></entry><entry><title type="html">源码阅读之LinkedList实现细节</title><link href="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BLinkedList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82" rel="alternate" type="text/html" title="源码阅读之LinkedList实现细节" /><published>2018-10-17T00:00:00+00:00</published><updated>2018-10-17T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BLinkedList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82</id><content type="html" xml:base="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BLinkedList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82"><![CDATA[<h4 id="0x00-描述">0x00 描述</h4>

<p><code class="language-plaintext highlighter-rouge">LinkedList</code> 是一个双向链表，这是一个基础的数据结构。打开 <code class="language-plaintext highlighter-rouge">LinkedList</code> 源码，可以看到它继承于 <code class="language-plaintext highlighter-rouge">AbstractSequentialList</code> ，这个是 <code class="language-plaintext highlighter-rouge">AbstractList</code> 的子类。同时也实现了 <code class="language-plaintext highlighter-rouge">List</code> 、<code class="language-plaintext highlighter-rouge">Deque</code> 、<code class="language-plaintext highlighter-rouge">Clone</code>、<code class="language-plaintext highlighter-rouge">Serializable</code> 接口。所以简化的类关系图可以表示为</p>

<p><img src="../images/linkedlist-structure.png" alt="linkedlist-structure" /></p>

<h5 id="关键属性">关键属性</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">size</code> 记录当前数组元素的个数</li>
  <li><code class="language-plaintext highlighter-rouge">first</code> 链表头指针</li>
  <li><code class="language-plaintext highlighter-rouge">last</code> 链表尾部指针</li>
  <li><code class="language-plaintext highlighter-rouge">modCount</code> 记录修改次数，这个字段是继承于 <code class="language-plaintext highlighter-rouge">AbstractList</code></li>
</ul>

<p><code class="language-plaintext highlighter-rouge">LinkedList</code> 是实现了序列化接口 <code class="language-plaintext highlighter-rouge">Serializable</code> ，而以上属性都被声明为 <code class="language-plaintext highlighter-rouge">transient</code> 表示这些字段不参与序列化。</p>

<h5 id="节点">节点</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="o">{</span>
	<span class="no">E</span> <span class="n">item</span><span class="o">;</span>
	<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">next</span><span class="o">;</span>
	<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">prev</span><span class="o">;</span>

	<span class="nc">Node</span><span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">prev</span><span class="o">,</span> <span class="no">E</span> <span class="n">element</span><span class="o">,</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">next</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">this</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="n">element</span><span class="o">;</span>
		<span class="k">this</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">next</span><span class="o">;</span>
		<span class="k">this</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">prev</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个节点类，记录链表中的节点的数据，有前指针、后指针和具体的数据元素。这个数据这里用泛型来表示了。</p>

<h5 id="构造方法">构造方法</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">LinkedList</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个是默认构造函数，创建一个空链表。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">LinkedList</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">this</span><span class="o">();</span>
	<span class="n">addAll</span><span class="o">(</span><span class="n">c</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这是通过列表来创建链表的。它调用了 <code class="language-plaintext highlighter-rouge">addAll</code> 方法。这个方法后文会讲到。</p>

<h4 id="0x01-常用方法">0x01 常用方法</h4>

<h5 id="addfirste-e">addFirst(E e)</h5>

<p>在链表头部添加节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">addFirst</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">linkFirst</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">linkFirst</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">newNode</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">&lt;&gt;(</span><span class="kc">null</span><span class="o">,</span> <span class="n">e</span><span class="o">,</span> <span class="n">f</span><span class="o">);</span>
	<span class="n">first</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="n">last</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">else</span>
		<span class="n">f</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="n">size</span><span class="o">++;</span>
	<span class="n">modCount</span><span class="o">++;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>它实际是调用了内部的一个私有方法 <code class="language-plaintext highlighter-rouge">linkFirst</code> 。只需要改变指针指向，时间复杂度O(1)。</p>

<h5 id="addlaste-e">addLast(E e)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">addLast</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">linkLast</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
<span class="o">}</span>

<span class="kt">void</span> <span class="nf">linkLast</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="n">last</span><span class="o">;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">newNode</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">&lt;&gt;(</span><span class="n">l</span><span class="o">,</span> <span class="n">e</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
	<span class="n">last</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">l</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="n">first</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">else</span>
		<span class="n">l</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="n">size</span><span class="o">++;</span>
	<span class="n">modCount</span><span class="o">++;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在链表尾部添加一个节点。它也是内部的 <code class="language-plaintext highlighter-rouge">linkLast</code> 方法。这方法执行效率也很高，只需要改变指针指向，时间复杂度是O(1)。</p>

<h5 id="adde-e">add(E e)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">add</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">linkLast</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
	<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以看出也是调用了 <code class="language-plaintext highlighter-rouge">linkLast</code> 方法。</p>

<h5 id="addint-index-e-element">add(int index, E element)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">add</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="no">E</span> <span class="n">element</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">checkPositionIndex</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>

	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">==</span> <span class="n">size</span><span class="o">)</span>
		<span class="n">linkLast</span><span class="o">(</span><span class="n">element</span><span class="o">);</span>
	<span class="k">else</span>
		<span class="nf">linkBefore</span><span class="o">(</span><span class="n">element</span><span class="o">,</span> <span class="n">node</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">checkPositionIndex</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(!</span><span class="n">isPositionIndex</span><span class="o">(</span><span class="n">index</span><span class="o">))</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">isPositionIndex</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">return</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">index</span> <span class="o">&lt;=</span> <span class="n">size</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在某个 <code class="language-plaintext highlighter-rouge">index</code> 前插入元素。</p>

<p>首先它会检查 <code class="language-plaintext highlighter-rouge">index</code> 是否正确。如果在 0~size 范围内的下标，那么就执行插入的方法；</p>

<p>它会判断如果 <code class="language-plaintext highlighter-rouge">index</code> 是等于 <code class="language-plaintext highlighter-rouge">size</code> 那么就在尾部插入元素，否则就在 <code class="language-plaintext highlighter-rouge">index</code> 所在节点前面插入元素。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">linkBefore</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">,</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">succ</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// assert succ != null;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">pred</span> <span class="o">=</span> <span class="n">succ</span><span class="o">.</span><span class="na">prev</span><span class="o">;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">newNode</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">&lt;&gt;(</span><span class="n">pred</span><span class="o">,</span> <span class="n">e</span><span class="o">,</span> <span class="n">succ</span><span class="o">);</span>
	<span class="n">succ</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">pred</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="n">first</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="k">else</span>
		<span class="n">pred</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="n">size</span><span class="o">++;</span>
	<span class="n">modCount</span><span class="o">++;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个方法在节点 <code class="language-plaintext highlighter-rouge">succ</code> 前面添加元素，时间复杂度为O(1)。</p>

<p>在调用这个方法之前需要获取到节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="nf">node</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// assert isElementIndex(index);</span>

	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="o">(</span><span class="n">size</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="o">))</span> <span class="o">{</span><span class="c1">//size &gt;&gt; 1 相当于 size/2</span>
		<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">index</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
			<span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
		<span class="k">return</span> <span class="n">x</span><span class="o">;</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">last</span><span class="o">;</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="n">index</span><span class="o">;</span> <span class="n">i</span><span class="o">--)</span>
			<span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="na">prev</span><span class="o">;</span>
		<span class="k">return</span> <span class="n">x</span><span class="o">;</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在链表中要通过下标查找一个节点，需要通过遍历。这里做了一个优化，当 <code class="language-plaintext highlighter-rouge">index</code> 是在前半部分时从链表头部开始遍历；如果 <code class="language-plaintext highlighter-rouge">index</code> 超过当前链表的一半时则从后面开始遍历查询，它的时间复杂度为O(n)。</p>

<h5 id="addallcollection-extends-e-c">addAll(Collection&lt;? extends E&gt; c)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">return</span> <span class="nf">addAll</span><span class="o">(</span><span class="n">size</span><span class="o">,</span> <span class="n">c</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在尾部插入一个列表，通过调用 <code class="language-plaintext highlighter-rouge">add(int,Collection)</code> 来实现。</p>

<h5 id="addallint-index-collection-extends-e-c">addAll(int index, Collection&lt;? extends E&gt; c)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//先检测 index 是否有效</span>
    <span class="n">checkPositionIndex</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
	
    <span class="c1">//以数组的形式获取到列表数据</span>
	<span class="nc">Object</span><span class="o">[]</span> <span class="n">a</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">toArray</span><span class="o">();</span>
	<span class="kt">int</span> <span class="n">numNew</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">numNew</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
		<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
	<span class="c1">//找到index的前向指针，后向指针</span>
	<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">pred</span><span class="o">,</span> <span class="n">succ</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">==</span> <span class="n">size</span><span class="o">)</span> <span class="o">{</span>
		<span class="n">succ</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
		<span class="n">pred</span> <span class="o">=</span> <span class="n">last</span><span class="o">;</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="n">succ</span> <span class="o">=</span> <span class="n">node</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
		<span class="n">pred</span> <span class="o">=</span> <span class="n">succ</span><span class="o">.</span><span class="na">prev</span><span class="o">;</span>
	<span class="o">}</span>
	<span class="c1">//依次把数组中的节点插入到列表中</span>
	<span class="k">for</span> <span class="o">(</span><span class="nc">Object</span> <span class="n">o</span> <span class="o">:</span> <span class="n">a</span><span class="o">)</span> <span class="o">{</span>
		<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span> <span class="no">E</span> <span class="n">e</span> <span class="o">=</span> <span class="o">(</span><span class="no">E</span><span class="o">)</span> <span class="n">o</span><span class="o">;</span>
		<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">newNode</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">&lt;&gt;(</span><span class="n">pred</span><span class="o">,</span> <span class="n">e</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">pred</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
			<span class="n">first</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
		<span class="k">else</span>
			<span class="n">pred</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
		<span class="n">pred</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">;</span>
	<span class="o">}</span>
	<span class="c1">//链接后向指针</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">succ</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
		<span class="n">last</span> <span class="o">=</span> <span class="n">pred</span><span class="o">;</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="n">pred</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">succ</span><span class="o">;</span>
		<span class="n">succ</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">pred</span><span class="o">;</span>
	<span class="o">}</span>
	<span class="c1">//更新size和modCount</span>
	<span class="n">size</span> <span class="o">+=</span> <span class="n">numNew</span><span class="o">;</span>
	<span class="n">modCount</span><span class="o">++;</span>
	<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个方法稍微复杂一点</p>

<ol>
  <li>先检测 <code class="language-plaintext highlighter-rouge">index</code> 是否有效</li>
  <li>以数组的形式获取到列表数据</li>
  <li>找到 <code class="language-plaintext highlighter-rouge">index</code> 所在节点的前向指针，后向指针</li>
  <li>依次把数组中的节点插入到列表中</li>
  <li>链接后向指针的数据</li>
  <li>更新 <code class="language-plaintext highlighter-rouge">size</code> 和 <code class="language-plaintext highlighter-rouge">modCount</code></li>
</ol>

<h5 id="getint-index">get(int index)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">get</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">checkElementIndex</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
	<span class="k">return</span> <span class="nf">node</span><span class="o">(</span><span class="n">index</span><span class="o">).</span><span class="na">item</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>获取 <code class="language-plaintext highlighter-rouge">index</code> 所在元素，通过 <code class="language-plaintext highlighter-rouge">node</code> 方法获取。前面分析可以知道，这个方法需要遍历，它的时间复杂度是O(n)。</p>

<h5 id="containsobject-o">contains(Object o)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">contains</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">return</span> <span class="nf">indexOf</span><span class="o">(</span><span class="n">o</span><span class="o">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">int</span> <span class="nf">indexOf</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
	<span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">o</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span> <span class="n">x</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">{</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">x</span><span class="o">.</span><span class="na">item</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
				<span class="k">return</span> <span class="n">index</span><span class="o">;</span>
			<span class="n">index</span><span class="o">++;</span>
		<span class="o">}</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span> <span class="n">x</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">{</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">o</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">x</span><span class="o">.</span><span class="na">item</span><span class="o">))</span>
				<span class="k">return</span> <span class="n">index</span><span class="o">;</span>
			<span class="n">index</span><span class="o">++;</span>
		<span class="o">}</span>
	<span class="o">}</span>
	<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>查询某个对象是否存在于该链表中是通过遍历来实现的。</p>

<h5 id="peek">peek()</h5>

<p>查看链表头节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">peek</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">f</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="peekfirst">peekFirst()</h5>

<p>查看链表头节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">peekFirst</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">f</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="peeklast">peekLast()</h5>

<p>查看链表尾部节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">peekLast</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="n">last</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">l</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">l</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="poll">poll()</h5>

<p>获取头节点，并把头节点从链表中删除</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">poll</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">unlinkFirst</span><span class="o">(</span><span class="n">f</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="no">E</span> <span class="nf">unlinkFirst</span><span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// assert f == first &amp;&amp; f != null;</span>
	<span class="kd">final</span> <span class="no">E</span> <span class="n">element</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">next</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
	<span class="n">f</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="n">f</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// help GC</span>
	<span class="n">first</span> <span class="o">=</span> <span class="n">next</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">next</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="n">last</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="k">else</span>
		<span class="n">next</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="n">size</span><span class="o">--;</span>
	<span class="n">modCount</span><span class="o">++;</span>
	<span class="k">return</span> <span class="n">element</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="pollfirst">pollFirst()</h5>

<p>同上</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">pollFirst</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">unlinkFirst</span><span class="o">(</span><span class="n">f</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="polllast">pollLast()</h5>

<p>获取尾部节点，并将尾部节点删除</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">pollLast</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="n">last</span><span class="o">;</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">l</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">unlinkLast</span><span class="o">(</span><span class="n">l</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="no">E</span> <span class="nf">unlinkLast</span><span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">l</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// assert l == last &amp;&amp; l != null;</span>
	<span class="kd">final</span> <span class="no">E</span> <span class="n">element</span> <span class="o">=</span> <span class="n">l</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">prev</span> <span class="o">=</span> <span class="n">l</span><span class="o">.</span><span class="na">prev</span><span class="o">;</span>
	<span class="n">l</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="n">l</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// help GC</span>
	<span class="n">last</span> <span class="o">=</span> <span class="n">prev</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">prev</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="n">first</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="k">else</span>
		<span class="n">prev</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="n">size</span><span class="o">--;</span>
	<span class="n">modCount</span><span class="o">++;</span>
	<span class="k">return</span> <span class="n">element</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="remove">remove()</h5>

<p>删除头节点</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">remove</span><span class="o">()</span> <span class="o">{</span>
	<span class="k">return</span> <span class="nf">removeFirst</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="no">E</span> <span class="nf">removeFirst</span><span class="o">()</span> <span class="o">{</span>
	<span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">f</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">NoSuchElementException</span><span class="o">();</span>
	<span class="k">return</span> <span class="nf">unlinkFirst</span><span class="o">(</span><span class="n">f</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="clear">clear()</h5>

<p>清空链表</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">()</span> <span class="o">{</span>
	<span class="c1">// Clearing all of the links between nodes is "unnecessary", but:</span>
	<span class="c1">// - helps a generational GC if the discarded nodes inhabit</span>
	<span class="c1">//   more than one generation</span>
	<span class="c1">// - is sure to free memory even if there is a reachable Iterator</span>
	<span class="k">for</span> <span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">first</span><span class="o">;</span> <span class="n">x</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span> <span class="o">)</span> <span class="o">{</span>
		<span class="nc">Node</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">next</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
		<span class="n">x</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
		<span class="n">x</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
		<span class="n">x</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
		<span class="n">x</span> <span class="o">=</span> <span class="n">next</span><span class="o">;</span>
	<span class="o">}</span>
	<span class="n">first</span> <span class="o">=</span> <span class="n">last</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
	<span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
	<span class="n">modCount</span><span class="o">++;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>遍历整个链表，将节点中的数据置为 <code class="language-plaintext highlighter-rouge">null</code> 。</p>

<h4 id="0x02-总结">0x02 总结</h4>

<ul>
  <li><code class="language-plaintext highlighter-rouge">LinkedList</code> 是一个双向链表，它是线程不安全的。</li>
  <li><code class="language-plaintext highlighter-rouge">LinkedList</code> 擅长插入、删除操作，时间复杂度是O(1)；但是如果事先不知道被插入的节点，则需要通过遍历来查询到该节点，而查询操作就不是很高效了，时间复杂度是O(n)。</li>
  <li><code class="language-plaintext highlighter-rouge">get</code> 方法需要遍历获得，<code class="language-plaintext highlighter-rouge">containts</code> 方法也需要遍历</li>
  <li>在链表头部或尾部插入节点效率要高，但是通过下标 <code class="language-plaintext highlighter-rouge">index</code> 插入节点则需要遍历找到插入的位置，再执行插入操作。</li>
</ul>]]></content><author><name>hylinux</name></author><category term="数据结构" /><summary type="html"><![CDATA[LinkedList源码分析]]></summary></entry><entry><title type="html">源码阅读之ArrayList实现细节</title><link href="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BArrayList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82" rel="alternate" type="text/html" title="源码阅读之ArrayList实现细节" /><published>2018-10-16T00:00:00+00:00</published><updated>2018-10-16T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BArrayList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82</id><content type="html" xml:base="https://hylinux1024.github.io/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E4%B9%8BArrayList%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82"><![CDATA[<h4 id="0x00-描述">0x00 描述</h4>

<p><code class="language-plaintext highlighter-rouge">ArrayList</code> 可以说是 <code class="language-plaintext highlighter-rouge">Java</code> 程序猿最为常用的一种数据结构了。<code class="language-plaintext highlighter-rouge">ArrayList</code> 是通过数组实现的，容量可以自增的线性表。而数组的优点是计算机可以通过下标计算访问地址，所以访问元素的速度是很快的，时间复杂度为O(1)；但数组并不擅长插入和删除操作，这些操作的时间复杂度是O(n)。因此 <code class="language-plaintext highlighter-rouge">ArrayList</code> 继承了数组这些特点。</p>

<h5 id="继承关系">继承关系</h5>

<p><code class="language-plaintext highlighter-rouge">ArrayList</code> 继承于 <code class="language-plaintext highlighter-rouge">AbstractList</code> 并实现了 <code class="language-plaintext highlighter-rouge">List</code> 、<code class="language-plaintext highlighter-rouge">RandomAccess</code>、<code class="language-plaintext highlighter-rouge">Cloneable</code> 和 <code class="language-plaintext highlighter-rouge">Serializable</code> 接口。</p>

<p>而 <code class="language-plaintext highlighter-rouge">AbstractList</code> 是继承于 <code class="language-plaintext highlighter-rouge">Collection</code> 接口。因此简单的关系图可以表达为</p>

<p><img src="../images/arraylist-structure.png" alt="array-structure" /></p>

<h5 id="重要属性">重要属性</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">elementData</code> 这个是存放数据的 <code class="language-plaintext highlighter-rouge">Object</code> 数组</li>
  <li><code class="language-plaintext highlighter-rouge">size</code> 记录当前数组元素的个数</li>
  <li><code class="language-plaintext highlighter-rouge">modCount</code> 用于记录修改次数，例如增加、删除等操作时此变量会自增。当这个变量异常变化时，会抛出 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code></li>
</ul>

<h5 id="构造方法">构造方法</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">ArrayList</span><span class="o">()</span> <span class="o">{</span>
	<span class="k">this</span><span class="o">.</span><span class="na">elementData</span> <span class="o">=</span> <span class="no">DEFAULTCAPACITY_EMPTY_ELEMENTDATA</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>默认构造函数初始化 <code class="language-plaintext highlighter-rouge">elementData</code> 大小为10 空数组。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">ArrayList</span><span class="o">(</span><span class="kt">int</span> <span class="n">initialCapacity</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">initialCapacity</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">elementData</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Object</span><span class="o">[</span><span class="n">initialCapacity</span><span class="o">];</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">initialCapacity</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">elementData</span> <span class="o">=</span> <span class="no">EMPTY_ELEMENTDATA</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Illegal Capacity: "</span><span class="o">+</span>                                      <span class="n">initialCapacity</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>此方法通过一个 <code class="language-plaintext highlighter-rouge">initialCapacity</code> 变量对数组进行初始化。当传入的 <code class="language-plaintext highlighter-rouge">initialCapacity</code> 大于0时，elementData就是初始化为大小为 <code class="language-plaintext highlighter-rouge">initialCapacity</code> 的空数组；否则就是初始化为大小为0的空数组。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">ArrayList</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">elementData</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">toArray</span><span class="o">();</span>
   	<span class="k">if</span> <span class="o">((</span><span class="n">size</span> <span class="o">=</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
		<span class="c1">// c.toArray might (incorrectly) not return Object[] (see 6260652)</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">elementData</span><span class="o">.</span><span class="na">getClass</span><span class="o">()</span> <span class="o">!=</span> <span class="nc">Object</span><span class="o">[].</span><span class="na">class</span><span class="o">)</span>
			<span class="n">elementData</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">size</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[].</span><span class="na">class</span><span class="o">);</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="c1">// replace with empty array.</span>
		<span class="k">this</span><span class="o">.</span><span class="na">elementData</span> <span class="o">=</span> <span class="no">EMPTY_ELEMENTDATA</span><span class="o">;</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>这个方法是通过一个 <code class="language-plaintext highlighter-rouge">Collection</code> 对象进行初始化的。这里调用了 <code class="language-plaintext highlighter-rouge">Arrays.copyOf</code> 方法将数组元素进行拷贝，并返回一个新的数组。后文会详细解析这个方法。</p>

<h4 id="0x01-常用方法">0x01 常用方法</h4>

<h5 id="adde-e">add(E e)</h5>

<p>给 <code class="language-plaintext highlighter-rouge">ArrayList</code> 添加一个元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">add</span><span class="o">(</span><span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">ensureCapacityInternal</span><span class="o">(</span><span class="n">size</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>  <span class="c1">// Increments modCount!!</span>
	<span class="n">elementData</span><span class="o">[</span><span class="n">size</span><span class="o">++]</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
	<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>添加元素之前，调用了 <code class="language-plaintext highlighter-rouge">ensureCapacityInternal</code> 方法，确保 <code class="language-plaintext highlighter-rouge">elementData</code> 数组有足够的空间。然后数组后面添加一个元素，并把元素个数 <code class="language-plaintext highlighter-rouge">size</code> 的值加1。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">ensureCapacityInternal</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">elementData</span> <span class="o">==</span> <span class="no">DEFAULTCAPACITY_EMPTY_ELEMENTDATA</span><span class="o">)</span> <span class="o">{</span>
		<span class="n">minCapacity</span> <span class="o">=</span> <span class="nc">Math</span><span class="o">.</span><span class="na">max</span><span class="o">(</span><span class="no">DEFAULT_CAPACITY</span><span class="o">,</span> <span class="n">minCapacity</span><span class="o">);</span>
	<span class="o">}</span>

	<span class="n">ensureExplicitCapacity</span><span class="o">(</span><span class="n">minCapacity</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在 <code class="language-plaintext highlighter-rouge">ensureCapacityInternal</code> 中先判断 <code class="language-plaintext highlighter-rouge">elementData</code> 是否为空数组，如果是，则取 <code class="language-plaintext highlighter-rouge">DEFAULT_CAPACITY</code> 与 <code class="language-plaintext highlighter-rouge">minCapacity</code> 的最大值作为数组的最小容量。</p>

<p>然后再执行 <code class="language-plaintext highlighter-rouge">ensureExplicitCapacity</code> 方法。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">ensureExplicitCapacity</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">modCount</span><span class="o">++;</span>

	<span class="c1">// overflow-conscious code</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">minCapacity</span> <span class="o">-</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">grow</span><span class="o">(</span><span class="n">minCapacity</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>先把 <code class="language-plaintext highlighter-rouge">modCount</code> 加1，表示对该列表进行了一次操作。</p>

<p><code class="language-plaintext highlighter-rouge">minCapacity</code> 表示目前需要的容量大小。如果它大于目前 <code class="language-plaintext highlighter-rouge">elementData</code> 的容量大小，那么就会执行 <code class="language-plaintext highlighter-rouge">grow</code> 方法增加数组容量。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">grow</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="c1">// overflow-conscious code</span>
	<span class="kt">int</span> <span class="n">oldCapacity</span> <span class="o">=</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
	<span class="kt">int</span> <span class="n">newCapacity</span> <span class="o">=</span> <span class="n">oldCapacity</span> <span class="o">+</span> <span class="o">(</span><span class="n">oldCapacity</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="o">);</span><span class="c1">//右移1位操作相当于除2</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">newCapacity</span> <span class="o">-</span> <span class="n">minCapacity</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">newCapacity</span> <span class="o">=</span> <span class="n">minCapacity</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">newCapacity</span> <span class="o">-</span> <span class="no">MAX_ARRAY_SIZE</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="n">newCapacity</span> <span class="o">=</span> <span class="n">hugeCapacity</span><span class="o">(</span><span class="n">minCapacity</span><span class="o">);</span>
	<span class="c1">// minCapacity is usually close to size, so this is a win:</span>
	<span class="n">elementData</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">newCapacity</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>该方法的逻辑是</p>

<ol>
  <li>先获取到 <code class="language-plaintext highlighter-rouge">newCapacity</code> ，它是原来容量大小的1.5倍</li>
  <li>如果需要的容量大小 <code class="language-plaintext highlighter-rouge">minCapacity</code> 大于原来容量的1.5，那么 <code class="language-plaintext highlighter-rouge">newCapacity</code> 就取 <code class="language-plaintext highlighter-rouge">minCapacity</code></li>
  <li>如果 <code class="language-plaintext highlighter-rouge">newCapacity</code> 还大于最大的容量，那么就执行 <code class="language-plaintext highlighter-rouge">hugeCapacity</code> 来计算得到容量大小</li>
  <li>最后调用 <code class="language-plaintext highlighter-rouge">Arrays.copyOf</code> 方法把 <code class="language-plaintext highlighter-rouge">elementData</code> 拷贝到一个新的数组中，这个新数组大小为 <code class="language-plaintext highlighter-rouge">newCapacity</code></li>
</ol>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">hugeCapacity</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">minCapacity</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="c1">// overflow</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">OutOfMemoryError</span><span class="o">();</span>
	<span class="k">return</span> <span class="o">(</span><span class="n">minCapacity</span> <span class="o">&gt;</span> <span class="no">MAX_ARRAY_SIZE</span><span class="o">)</span> <span class="o">?</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span> <span class="o">:</span> <span class="no">MAX_ARRAY_SIZE</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>因此在调用 <code class="language-plaintext highlighter-rouge">add</code> 方法时，如果<strong>当前数组 <code class="language-plaintext highlighter-rouge">elementData</code> 的容量不够时，就会调用扩容的 <code class="language-plaintext highlighter-rouge">grow</code> 方法，把数组扩大为原来的1.5倍的大小。</strong></p>

<h5 id="addint-index-e-element">add(int index, E element)</h5>

<p>在指定的 <code class="language-plaintext highlighter-rouge">index</code> 位置上添加一个元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">add</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="no">E</span> <span class="n">element</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&gt;</span> <span class="n">size</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>

	<span class="n">ensureCapacityInternal</span><span class="o">(</span><span class="n">size</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>  <span class="c1">// Increments modCount!!</span>
	<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">size</span> <span class="o">-</span> <span class="n">index</span><span class="o">);</span>
	<span class="n">elementData</span><span class="o">[</span><span class="n">index</span><span class="o">]</span> <span class="o">=</span> <span class="n">element</span><span class="o">;</span>
	<span class="n">size</span><span class="o">++;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>通过上面的 <code class="language-plaintext highlighter-rouge">add</code> 方法的走读，这个方法就很好理解了。</p>

<p>先对 <code class="language-plaintext highlighter-rouge">index</code> 参数的有效性进行判断；</p>

<p>然后执行 <code class="language-plaintext highlighter-rouge">ensureCapacityInternal</code> 确保数组的容量大小是足够的，此时 <code class="language-plaintext highlighter-rouge">modCount</code> 也会自增；</p>

<p>再执行 <code class="language-plaintext highlighter-rouge">System.arraycopy</code> 方法把数组元素从 <code class="language-plaintext highlighter-rouge">index</code> 的位置后移1位；（<code class="language-plaintext highlighter-rouge">System.arraycopy</code> 函数后文还会讲到）</p>

<p>最后在 <code class="language-plaintext highlighter-rouge">index</code> 位置上赋值，并把 <code class="language-plaintext highlighter-rouge">size</code> 加 1。</p>

<h5 id="addallcollection-extends-e-c">addAll(Collection&lt;? extends E&gt; c)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
	<span class="nc">Object</span><span class="o">[]</span> <span class="n">a</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">toArray</span><span class="o">();</span>
	<span class="kt">int</span> <span class="n">numNew</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
	<span class="n">ensureCapacityInternal</span><span class="o">(</span><span class="n">size</span> <span class="o">+</span> <span class="n">numNew</span><span class="o">);</span>  <span class="c1">// Increments modCount</span>
	<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">size</span><span class="o">,</span> <span class="n">numNew</span><span class="o">);</span>
	<span class="n">size</span> <span class="o">+=</span> <span class="n">numNew</span><span class="o">;</span>
	<span class="k">return</span> <span class="n">numNew</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">addAll</code> 方法把一个 <code class="language-plaintext highlighter-rouge">Collection</code> 对象添加到列表中来。</p>

<p>它会先把 <code class="language-plaintext highlighter-rouge">Collection</code> 对象通过 <code class="language-plaintext highlighter-rouge">toArray</code> 方法转化为数组，然后再调用 <code class="language-plaintext highlighter-rouge">System.arraycopy</code> 进行数据的移动。</p>

<h5 id="addallint-index-collection-extends-e-c">addAll(int index, Collection&lt;? extends E&gt; c)</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&gt;</span> <span class="n">size</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>

	<span class="nc">Object</span><span class="o">[]</span> <span class="n">a</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">toArray</span><span class="o">();</span>
	<span class="kt">int</span> <span class="n">numNew</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
	<span class="n">ensureCapacityInternal</span><span class="o">(</span><span class="n">size</span> <span class="o">+</span> <span class="n">numNew</span><span class="o">);</span>  <span class="c1">// Increments modCount</span>

	<span class="kt">int</span> <span class="n">numMoved</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="n">index</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">numMoved</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">index</span> <span class="o">+</span> <span class="n">numNew</span><span class="o">,</span> <span class="n">numMoved</span><span class="o">);</span>
	
    <span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">,</span> <span class="n">numNew</span><span class="o">);</span>
	<span class="n">size</span> <span class="o">+=</span> <span class="n">numNew</span><span class="o">;</span>
	<span class="k">return</span> <span class="n">numNew</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>在 <code class="language-plaintext highlighter-rouge">index</code> 位置上添加一个列表</p>

<p>它与上面 <code class="language-plaintext highlighter-rouge">addAll</code> 方法的区别就是先从 <code class="language-plaintext highlighter-rouge">index</code> 开始移动 <code class="language-plaintext highlighter-rouge">numNew</code> 个位置，即空出 <code class="language-plaintext highlighter-rouge">numNew</code> 个位置。</p>

<p>然后再空出的 <code class="language-plaintext highlighter-rouge">numNew</code> 位置上添加元素。</p>

<h5 id="removeint-index">remove(int index)</h5>

<p>删除指定 <code class="language-plaintext highlighter-rouge">index</code> 位置上的元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="no">E</span> <span class="nf">remove</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&gt;=</span> <span class="n">size</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>

	<span class="n">modCount</span><span class="o">++;</span>
	<span class="no">E</span> <span class="n">oldValue</span> <span class="o">=</span> <span class="o">(</span><span class="no">E</span><span class="o">)</span> <span class="n">elementData</span><span class="o">[</span><span class="n">index</span><span class="o">];</span>

	<span class="kt">int</span> <span class="n">numMoved</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="n">index</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">numMoved</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">,</span>
                             <span class="n">numMoved</span><span class="o">);</span>
	<span class="n">elementData</span><span class="o">[--</span><span class="n">size</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// clear to let GC do its work</span>

	<span class="k">return</span> <span class="n">oldValue</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>执行流程为</p>

<ol>
  <li>检查 <code class="language-plaintext highlighter-rouge">index</code> 有效性，无效则抛出 <code class="language-plaintext highlighter-rouge">IndexOutOfBoundsException</code> 异常</li>
  <li><code class="language-plaintext highlighter-rouge">modCount</code> 自增</li>
  <li>通过 <code class="language-plaintext highlighter-rouge">index</code> 下标取出元素</li>
  <li>计算 <code class="language-plaintext highlighter-rouge">index</code> 后面需要移动的元素个数</li>
  <li>通过 <code class="language-plaintext highlighter-rouge">System.arraycopy</code> 将 <code class="language-plaintext highlighter-rouge">index</code> 后面的元素都往前移动1位</li>
  <li>最后把末尾元素置位 <code class="language-plaintext highlighter-rouge">null</code>，并把 <code class="language-plaintext highlighter-rouge">size</code> 的值减 1。</li>
</ol>

<h5 id="removeobject-o">remove(Object o)</h5>

<p>通过一个元素对象进行删除</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">remove</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">o</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">index</span><span class="o">++)</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">index</span><span class="o">]</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
				<span class="n">fastRemove</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
				<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
			<span class="o">}</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">index</span><span class="o">++)</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">o</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">index</span><span class="o">]))</span> <span class="o">{</span>
				<span class="n">fastRemove</span><span class="o">(</span><span class="n">index</span><span class="o">);</span>
				<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
			<span class="o">}</span>
	<span class="o">}</span>
	<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>当传一个元素对象进行删除操作时，需要遍历数组，找到该元素在列表中的位置 <code class="language-plaintext highlighter-rouge">index</code>；</p>

<p>然后通过 <code class="language-plaintext highlighter-rouge">fastRemove</code> 方法进行删除。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">fastRemove</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">modCount</span><span class="o">++;</span>
	<span class="kt">int</span> <span class="n">numMoved</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="n">index</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">numMoved</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="o">,</span> <span class="n">elementData</span><span class="o">,</span> <span class="n">index</span><span class="o">,</span> <span class="n">numMoved</span><span class="o">);</span>
	<span class="n">elementData</span><span class="o">[--</span><span class="n">size</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// clear to let GC do its work</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="clear">clear()</h5>

<p>清空列表</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">()</span> <span class="o">{</span>
	<span class="n">modCount</span><span class="o">++;</span>

	<span class="c1">// clear to let GC do its work</span>
	<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
		<span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

	<span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h5 id="sublistint-fromindex-int-toindex">subList(int fromIndex, int toIndex)</h5>

<p>获取子列表</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="nf">subList</span><span class="o">(</span><span class="kt">int</span> <span class="n">fromIndex</span><span class="o">,</span> <span class="kt">int</span> <span class="n">toIndex</span><span class="o">)</span> <span class="o">{</span>
	<span class="n">subListRangeCheck</span><span class="o">(</span><span class="n">fromIndex</span><span class="o">,</span> <span class="n">toIndex</span><span class="o">,</span> <span class="n">size</span><span class="o">);</span>
	<span class="k">return</span> <span class="k">new</span> <span class="nf">SubList</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">fromIndex</span><span class="o">,</span> <span class="n">toIndex</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">static</span> <span class="kt">void</span> <span class="nf">subListRangeCheck</span><span class="o">(</span><span class="kt">int</span> <span class="n">fromIndex</span><span class="o">,</span> <span class="kt">int</span> <span class="n">toIndex</span><span class="o">,</span> <span class="kt">int</span> <span class="n">size</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">fromIndex</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="s">"fromIndex = "</span> <span class="o">+</span> <span class="n">fromIndex</span><span class="o">);</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">toIndex</span> <span class="o">&gt;</span> <span class="n">size</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="s">"toIndex = "</span> <span class="o">+</span> <span class="n">toIndex</span><span class="o">);</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">fromIndex</span> <span class="o">&gt;</span> <span class="n">toIndex</span><span class="o">)</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"fromIndex("</span> <span class="o">+</span> <span class="n">fromIndex</span> <span class="o">+</span><span class="s">") &gt; toIndex("</span> <span class="o">+</span> <span class="n">toIndex</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>首先检查下标是否正确，然后构造一个 <code class="language-plaintext highlighter-rouge">SubList</code> 对象，这是一个内部类。</p>

<p><code class="language-plaintext highlighter-rouge">SubList</code> 也是继承于 <code class="language-plaintext highlighter-rouge">AbstractList</code>。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">class</span> <span class="nc">SubList</span> <span class="kd">extends</span> <span class="nc">AbstractList</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="kd">implements</span> <span class="nc">RandomAccess</span> <span class="o">{</span>
        <span class="kd">private</span> <span class="kd">final</span> <span class="nc">AbstractList</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">parent</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">parentOffset</span><span class="o">;</span>
        <span class="kd">private</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">offset</span><span class="o">;</span>
        <span class="kt">int</span> <span class="n">size</span><span class="o">;</span>

        <span class="nc">SubList</span><span class="o">(</span><span class="nc">AbstractList</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="n">parent</span><span class="o">,</span>
                <span class="kt">int</span> <span class="n">offset</span><span class="o">,</span> <span class="kt">int</span> <span class="n">fromIndex</span><span class="o">,</span> <span class="kt">int</span> <span class="n">toIndex</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">parent</span> <span class="o">=</span> <span class="n">parent</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">parentOffset</span> <span class="o">=</span> <span class="n">fromIndex</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">offset</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">fromIndex</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">size</span> <span class="o">=</span> <span class="n">toIndex</span> <span class="o">-</span> <span class="n">fromIndex</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">=</span> <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="no">E</span> <span class="nf">set</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">!=</span> <span class="k">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
            <span class="no">E</span> <span class="n">oldValue</span> <span class="o">=</span> <span class="o">(</span><span class="no">E</span><span class="o">)</span> <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">elementData</span><span class="o">[</span><span class="n">offset</span> <span class="o">+</span> <span class="n">index</span><span class="o">];</span>
            <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">elementData</span><span class="o">[</span><span class="n">offset</span> <span class="o">+</span> <span class="n">index</span><span class="o">]</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
            <span class="k">return</span> <span class="n">oldValue</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="no">E</span> <span class="nf">get</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
              <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">!=</span> <span class="k">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
            <span class="k">return</span> <span class="o">(</span><span class="no">E</span><span class="o">)</span> <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">elementData</span><span class="o">[</span><span class="n">offset</span> <span class="o">+</span> <span class="n">index</span><span class="o">];</span>
        <span class="o">}</span>

        <span class="o">...</span>

        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">add</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="no">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">!=</span> <span class="k">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
            <span class="n">parent</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">parentOffset</span> <span class="o">+</span> <span class="n">index</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
            <span class="k">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">=</span> <span class="n">parent</span><span class="o">.</span><span class="na">modCount</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">++;</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="no">E</span> <span class="nf">remove</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">!=</span> <span class="k">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
            <span class="no">E</span> <span class="n">result</span> <span class="o">=</span> <span class="n">parent</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">parentOffset</span> <span class="o">+</span> <span class="n">index</span><span class="o">);</span>
            <span class="k">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">=</span> <span class="n">parent</span><span class="o">.</span><span class="na">modCount</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">--;</span>
            <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="o">...</span>

        <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">return</span> <span class="nf">addAll</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">,</span> <span class="n">c</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">addAll</span><span class="o">(</span><span class="kt">int</span> <span class="n">index</span><span class="o">,</span> <span class="nc">Collection</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">outOfBoundsMsg</span><span class="o">(</span><span class="n">index</span><span class="o">));</span>
            <span class="kt">int</span> <span class="n">cSize</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">size</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">cSize</span><span class="o">==</span><span class="mi">0</span><span class="o">)</span>
                <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>

            <span class="k">if</span> <span class="o">(</span><span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">!=</span> <span class="k">this</span><span class="o">.</span><span class="na">modCount</span><span class="o">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
            <span class="n">parent</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="n">parentOffset</span> <span class="o">+</span> <span class="n">index</span><span class="o">,</span> <span class="n">c</span><span class="o">);</span>
            <span class="k">this</span><span class="o">.</span><span class="na">modCount</span> <span class="o">=</span> <span class="n">parent</span><span class="o">.</span><span class="na">modCount</span><span class="o">;</span>
            <span class="k">this</span><span class="o">.</span><span class="na">size</span> <span class="o">+=</span> <span class="n">cSize</span><span class="o">;</span>
            <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
        <span class="o">}</span>
    
        <span class="o">...</span>
    <span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">SubList</code> 构造方法需要一个父列表。在获取、添加、删除元素的方法中实际上都是调用父列表中的方法。</p>

<p>不过这些操作的方法中会判断 <code class="language-plaintext highlighter-rouge">modCount</code> 的值是否已经变化，如果异常改变了，那么就会抛出 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code> 异常。</p>

<h5 id="foreachconsumer-super-e-action">forEach(Consumer&lt;? super E&gt; action)</h5>

<p>遍历列表元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">forEach</span><span class="o">(</span><span class="nc">Consumer</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="no">E</span><span class="o">&gt;</span> <span class="n">action</span><span class="o">)</span> <span class="o">{</span>
	<span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">action</span><span class="o">);</span>
	<span class="kd">final</span> <span class="kt">int</span> <span class="n">expectedModCount</span> <span class="o">=</span> <span class="n">modCount</span><span class="o">;</span>
	<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span>
	<span class="kd">final</span> <span class="no">E</span><span class="o">[]</span> <span class="n">elementData</span> <span class="o">=</span> <span class="o">(</span><span class="no">E</span><span class="o">[])</span> <span class="k">this</span><span class="o">.</span><span class="na">elementData</span><span class="o">;</span>
	<span class="kd">final</span> <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span><span class="o">;</span>
	<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="o">;</span> <span class="n">modCount</span> <span class="o">==</span> <span class="n">expectedModCount</span> <span class="o">&amp;&amp;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
		<span class="n">action</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]);</span>
	<span class="o">}</span>
	<span class="c1">// Android-note:</span>
	<span class="c1">// Iterator will not throw a CME if we add something while iterating over the *last* element</span>
	<span class="c1">// forEach will throw a CME in this case.</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">modCount</span> <span class="o">!=</span> <span class="n">expectedModCount</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>同样地，此方法中如果 <code class="language-plaintext highlighter-rouge">modCount</code> 被异常修改了（例如在其他线程中执行了 <code class="language-plaintext highlighter-rouge">add</code> 方法）那么就会抛出 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code> 异常。</p>

<h5 id="iterator">iterator()</h5>

<p>获取遍历器</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Iterator</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="nf">iterator</span><span class="o">()</span> <span class="o">{</span>
	<span class="k">return</span> <span class="k">new</span> <span class="nf">Itr</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Itr</code> 是一个内部类，实现了 <code class="language-plaintext highlighter-rouge">Iterator</code> 接口。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">class</span> <span class="nc">Itr</span> <span class="kd">implements</span> <span class="nc">Iterator</span><span class="o">&lt;</span><span class="no">E</span><span class="o">&gt;</span> <span class="o">{</span>
	<span class="c1">// Android-changed: Add "limit" field to detect end of iteration.</span>
	<span class="c1">// The "limit" of this iterator. This is the size of the list at the time the</span>
	<span class="c1">// iterator was created. Adding &amp; removing elements will invalidate the iteration</span>
	<span class="c1">// anyway (and cause next() to throw) so saving this value will guarantee that the</span>
	<span class="c1">// value of hasNext() remains stable and won't flap between true and false when elements</span>
	<span class="c1">// are added and removed from the list.</span>
	<span class="kd">protected</span> <span class="kt">int</span> <span class="n">limit</span> <span class="o">=</span> <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">size</span><span class="o">;</span>

	<span class="kt">int</span> <span class="n">cursor</span><span class="o">;</span>       <span class="c1">// index of next element to return</span>
	<span class="kt">int</span> <span class="n">lastRet</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span> <span class="c1">// index of last element returned; -1 if no such</span>
	<span class="kt">int</span> <span class="n">expectedModCount</span> <span class="o">=</span> <span class="n">modCount</span><span class="o">;</span>

	<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">hasNext</span><span class="o">()</span> <span class="o">{</span>
		<span class="k">return</span> <span class="n">cursor</span> <span class="o">&lt;</span> <span class="n">limit</span><span class="o">;</span>
	<span class="o">}</span>

	<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span>
	<span class="kd">public</span> <span class="no">E</span> <span class="nf">next</span><span class="o">()</span> <span class="o">{</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">modCount</span> <span class="o">!=</span> <span class="n">expectedModCount</span><span class="o">)</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
		<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">;</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="n">limit</span><span class="o">)</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">NoSuchElementException</span><span class="o">();</span>
		<span class="nc">Object</span><span class="o">[]</span> <span class="n">elementData</span> <span class="o">=</span> <span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">elementData</span><span class="o">;</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="n">elementData</span><span class="o">.</span><span class="na">length</span><span class="o">)</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
		<span class="n">cursor</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
		<span class="k">return</span> <span class="o">(</span><span class="no">E</span><span class="o">)</span> <span class="n">elementData</span><span class="o">[</span><span class="n">lastRet</span> <span class="o">=</span> <span class="n">i</span><span class="o">];</span>
	<span class="o">}</span>
	<span class="kd">public</span> <span class="kt">void</span> <span class="nf">remove</span><span class="o">()</span> <span class="o">{</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">lastRet</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalStateException</span><span class="o">();</span>
		<span class="k">if</span> <span class="o">(</span><span class="n">modCount</span> <span class="o">!=</span> <span class="n">expectedModCount</span><span class="o">)</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>

		<span class="k">try</span> <span class="o">{</span>
			<span class="nc">ArrayList</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">lastRet</span><span class="o">);</span>
			<span class="n">cursor</span> <span class="o">=</span> <span class="n">lastRet</span><span class="o">;</span>
			<span class="n">lastRet</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
			<span class="n">expectedModCount</span> <span class="o">=</span> <span class="n">modCount</span><span class="o">;</span>
			<span class="n">limit</span><span class="o">--;</span>
		<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IndexOutOfBoundsException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
			<span class="k">throw</span> <span class="k">new</span> <span class="nf">ConcurrentModificationException</span><span class="o">();</span>
		<span class="o">}</span>
	<span class="o">}</span>

	<span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>该类中 <code class="language-plaintext highlighter-rouge">cursor</code> 属性记录了当前迭代的位置，每调用一次 <code class="language-plaintext highlighter-rouge">next</code> 方法都会加 1，<code class="language-plaintext highlighter-rouge">lastRet</code> 则记录了上一次的元素位置。</p>

<p><code class="language-plaintext highlighter-rouge">remove</code> 方法则是通过调用外部类的 <code class="language-plaintext highlighter-rouge">remove</code> 方法来实现的。</p>

<p>以上两个方法中也需要注意 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code> 异常的发生。</p>

<h5 id="containsobject-o">contains(Object o)</h5>

<p>检测是否包含元素</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">contains</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">return</span> <span class="nf">indexOf</span><span class="o">(</span><span class="n">o</span><span class="o">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kt">int</span> <span class="nf">indexOf</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">(</span><span class="n">o</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]==</span><span class="kc">null</span><span class="o">)</span>
				<span class="k">return</span> <span class="n">i</span><span class="o">;</span>
	<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
			<span class="k">if</span> <span class="o">(</span><span class="n">o</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">elementData</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span>
				<span class="k">return</span> <span class="n">i</span><span class="o">;</span>
	<span class="o">}</span>
	<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以看出要检测一个元素是否在列表中，是通过遍历来实现的。</p>

<h5 id="systemarraycopy">System.arraycopy</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kd">native</span> <span class="kt">void</span> <span class="nf">arraycopy</span><span class="o">(</span><span class="nc">Object</span> <span class="n">src</span><span class="o">,</span>  <span class="kt">int</span>  <span class="n">srcPos</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">dest</span><span class="o">,</span> <span class="kt">int</span> <span class="n">destPos</span><span class="o">,</span><span class="kt">int</span> <span class="n">length</span><span class="o">);</span>
</code></pre></div></div>

<p>这是数组拷贝函数，是 <code class="language-plaintext highlighter-rouge">native</code> 函数，它经过虚拟机优化的，效率比较高。在 <code class="language-plaintext highlighter-rouge">ArrayList</code> 中移动元素就是通过这个方法。</p>

<h5 id="arrayscopyof">Arrays.copyOf</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="no">T</span><span class="o">[]</span> <span class="nf">copyOf</span><span class="o">(</span><span class="no">T</span><span class="o">[]</span> <span class="n">original</span><span class="o">,</span> <span class="kt">int</span> <span class="n">newLength</span><span class="o">)</span> <span class="o">{</span>
	<span class="k">return</span> <span class="o">(</span><span class="no">T</span><span class="o">[])</span> <span class="n">copyOf</span><span class="o">(</span><span class="n">original</span><span class="o">,</span> <span class="n">newLength</span><span class="o">,</span> <span class="n">original</span><span class="o">.</span><span class="na">getClass</span><span class="o">());</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="kd">static</span> <span class="o">&lt;</span><span class="no">T</span><span class="o">,</span><span class="no">U</span><span class="o">&gt;</span> <span class="no">T</span><span class="o">[]</span> <span class="nf">copyOf</span><span class="o">(</span><span class="no">U</span><span class="o">[]</span> <span class="n">original</span><span class="o">,</span> <span class="kt">int</span> <span class="n">newLength</span><span class="o">,</span> <span class="nc">Class</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="no">T</span><span class="o">[]&gt;</span> <span class="n">newType</span><span class="o">)</span> <span class="o">{</span>
	<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span>
	<span class="no">T</span><span class="o">[]</span> <span class="n">copy</span> <span class="o">=</span> <span class="o">((</span><span class="nc">Object</span><span class="o">)</span><span class="n">newType</span> <span class="o">==</span> <span class="o">(</span><span class="nc">Object</span><span class="o">)</span><span class="nc">Object</span><span class="o">[].</span><span class="na">class</span><span class="o">)</span> <span class="o">?</span> <span class="o">(</span><span class="no">T</span><span class="o">[])</span> <span class="k">new</span> <span class="nc">Object</span><span class="o">[</span><span class="n">newLength</span><span class="o">]</span> <span class="o">:</span> <span class="o">(</span><span class="no">T</span><span class="o">[])</span> <span class="nc">Array</span><span class="o">.</span><span class="na">newInstance</span><span class="o">(</span><span class="n">newType</span><span class="o">.</span><span class="na">getComponentType</span><span class="o">(),</span> <span class="n">newLength</span><span class="o">);</span>
	<span class="nc">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span><span class="n">original</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">copy</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="nc">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="n">original</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="n">newLength</span><span class="o">));</span>
	<span class="k">return</span> <span class="n">copy</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>可以看出 <code class="language-plaintext highlighter-rouge">copyOf</code> 函数最终调用的是 <code class="language-plaintext highlighter-rouge">System.arraycopy</code> 方法。本文中 <code class="language-plaintext highlighter-rouge">grow</code> 方法就是调用 <code class="language-plaintext highlighter-rouge">copyOf</code> 来实现扩容的。</p>

<h4 id="0x02-总结">0x02 总结</h4>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ArrayList</code> 是基于数组实现的线性表，它支持自动扩容，每次增加原来容量的1.5倍。</li>
  <li>通过下标获取元素操作效率高，而删除和插入操作则需要移动元素，效率不高。</li>
  <li><code class="language-plaintext highlighter-rouge">remove</code> 函数通过对象删除元素时需要遍历列表，而通过下标 <code class="language-plaintext highlighter-rouge">index</code> 删除元素比通过对象删除元素的效率要高。</li>
  <li><code class="language-plaintext highlighter-rouge">containts</code> 与 <code class="language-plaintext highlighter-rouge">clear</code>方法需要遍历。</li>
  <li><code class="language-plaintext highlighter-rouge">subList</code> 获取到子列表，对子列表的修改同样也会修改父列表。</li>
  <li><code class="language-plaintext highlighter-rouge">ArrayList</code> 没有同步锁，在多线程操作时需要注意 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code> 异常。</li>
  <li>如果在同一线程中对 <code class="language-plaintext highlighter-rouge">ArrayList</code> 操作时引起 <code class="language-plaintext highlighter-rouge">modCount</code> 异常改变时，也要注意 <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code> ，这时候要检查代码逻辑问题。</li>
</ul>]]></content><author><name>hylinux</name></author><category term="数据结构" /><summary type="html"><![CDATA[从最简单的ArrayList开始学习Java]]></summary></entry><entry><title type="html">一文看懂ConstraintLayout的用法</title><link href="https://hylinux1024.github.io/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82ConstraintLayout%E7%9A%84%E7%94%A8%E6%B3%95" rel="alternate" type="text/html" title="一文看懂ConstraintLayout的用法" /><published>2018-10-02T00:00:00+00:00</published><updated>2018-10-02T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82ConstraintLayout%E7%9A%84%E7%94%A8%E6%B3%95</id><content type="html" xml:base="https://hylinux1024.github.io/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82ConstraintLayout%E7%9A%84%E7%94%A8%E6%B3%95"><![CDATA[<p><code class="language-plaintext highlighter-rouge">ConstraintLayout</code> 相对于<code class="language-plaintext highlighter-rouge">RelativeLayout</code>来说性能更好，布局上也更加灵活。在最新的Google Android开发文档中是推荐使用<code class="language-plaintext highlighter-rouge">ConstraintLayout</code>的，下面来看看具体用法。</p>

<h4 id="0x00-相对位置relative-positioning">0x00 相对位置(Relative positioning)</h4>

<p>这个比较简单，看图解释，假设控件B要放在控件A的右侧，可以使用<code class="language-plaintext highlighter-rouge">layout_constraintLeft_toRightOf</code>属性。</p>

<p><img src="../images/relative-positioning.png" alt="" /></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/buttonA"</span> <span class="err">...</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/buttonB"</span> <span class="err">...</span>
     <span class="na">app:layout_constraintLeft_toRightOf=</span><span class="s">"@+id/buttonA"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>看图2可以了解控件约束属性代表的含义。</p>

<p><img src="../images/relative-positioning-constraint.png" alt="" /></p>

<p>类似相对位置的约束属性有：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintLeft_toLeftOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintLeft_toRightOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintRight_toLeftOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintRight_toRightOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintTop_toTopOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintTop_toBottomOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintBottom_toTopOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintBottom_toBottomOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintBaseline_toBaselineOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintStart_toEndOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintStart_toStartOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintEnd_toStartOf</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintEnd_toEndOf</code></li>
</ul>

<h4 id="0x01-外边距margins">0x01 外边距(Margins)</h4>

<p>这个属性也好理解，看图3</p>

<p><img src="../images/relative-positioning-margins.png" alt="" /></p>

<p>可以通过以下属性设置一个控件相对另一个控件的外边距：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginStart</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginEnd</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginLeft</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginTop</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginRight</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:layout_marginBottom</code></li>
</ul>

<p>属性值必须是大于或者等于0。</p>

<p>接一下看一个<code class="language-plaintext highlighter-rouge">RelativeLayout</code> 没有的属性：</p>

<h4 id="0x02-margins-when-connected-to-a-gone-widget">0x02 Margins when connected to a GONE widget</h4>

<p>当一个相对的控件隐藏时，<code class="language-plaintext highlighter-rouge">ConstraintLayout</code>也可以设置一个不同的边距：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginStart</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginEnd</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginLeft</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginTop</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginRight</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_goneMarginBottom</code></li>
</ul>

<p>具体的栗子下面会讲到。</p>

<h4 id="0x03-centering-positioning-and-bias">0x03 Centering positioning and bias</h4>

<p>居中以及设置偏差</p>

<p><img src="../images/centering-positioning.png" alt="" /></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>
	<span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/button"</span> <span class="err">...</span>
		<span class="na">app:layout_constraintLeft_toLeftOf=</span><span class="s">"parent"</span>
		<span class="na">app:layout_constraintRight_toRightOf=</span><span class="s">"parent"</span><span class="nt">/&gt;</span>                                     
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<p>还可以设置bias属性，表示子控件相对父控件的位置倾向，可以使用属性：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintHorizontal_bias</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintVertical_bias</code></li>
</ul>

<p><img src="../images/centering-positioning-with-bias.png" alt="" /></p>

<p>假设设置控件A相对父控件横向偏差是30%：</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>
             <span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/button"</span> <span class="err">...</span>
                 <span class="na">app:layout_constraintHorizontal_bias=</span><span class="s">"0.3"</span>
                 <span class="na">app:layout_constraintLeft_toLeftOf=</span><span class="s">"parent"</span>
                 <span class="na">app:layout_constraintRight_toRightOf=</span><span class="s">"parent"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<h4 id="0x04-弧形定位circular-positioning">0x04 弧形定位(Circular positioning)</h4>

<p>这个属性是在1.1版本添加的。</p>

<p><img src="../images/circular-positioning.png" alt="" /></p>

<p>可以使用属性有：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintCircle</code> : 相对控件的id</li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintCircleRadius</code> : 相对控件中心的距离，也就是圆的半径</li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintCircleAngle</code> : 相对夹角 (从 0 ~ 360度)</li>
</ul>

<p>例如，图6代码示例</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/buttonA"</span> <span class="err">...</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;Button</span> <span class="na">android:id=</span><span class="s">"@+id/buttonB"</span> <span class="err">...</span>
	<span class="na">app:layout_constraintCircle=</span><span class="s">"@+id/buttonA"</span>
	<span class="na">app:layout_constraintCircleRadius=</span><span class="s">"100dp"</span>
	<span class="na">app:layout_constraintCircleAngle=</span><span class="s">"45"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<h4 id="0x05-visibility-behavior">0x05 Visibility behavior</h4>

<p><img src="/Users/lancelot/hylinux1024.github.io/images/visibility-behavior.png" alt="" /></p>

<p>一般情况下，设置<code class="language-plaintext highlighter-rouge">GONE</code>属性后，控件就不会出现在布局中了，B控件对A控件的margin属性也就没有作用了。</p>

<p>但是<code class="language-plaintext highlighter-rouge">ConstraintLayout</code> 能对已经设置<code class="language-plaintext highlighter-rouge">GONE</code>属性的控件进行特殊处理。当A控件设置<code class="language-plaintext highlighter-rouge">GONE</code>之后，A控件相当于变成了一个点，<strong>B控件相对于对A的约束仍然是起作用的</strong>。图7的代码示例，A控件设置成了<code class="language-plaintext highlighter-rouge">GONE</code>,当B控件的<code class="language-plaintext highlighter-rouge">margin</code>属性还是有作用的。</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>
    <span class="nt">&lt;Button</span>
        <span class="na">android:id=</span><span class="s">"@+id/buttonA"</span>
        <span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
        <span class="na">android:text=</span><span class="s">"button A"</span>
        <span class="na">android:visibility=</span><span class="s">"gone"</span>
        <span class="na">app:layout_constraintLeft_toLeftOf=</span><span class="s">"parent"</span> <span class="nt">/&gt;</span>
	<span class="c">&lt;!--当A控件设置Gone之后，B控件的margin属性是起作用的，即左边距还是30dp--&gt;</span>
    <span class="nt">&lt;Button</span>
        <span class="na">android:id=</span><span class="s">"@+id/buttonB"</span>
        <span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_margin=</span><span class="s">"30dp"</span>
        <span class="na">android:text=</span><span class="s">"button B"</span>
        <span class="na">app:layout_constraintLeft_toRightOf=</span><span class="s">"@+id/buttonA"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<p>然而有时候，<strong>B控件是不希望相对于隐藏控件的属性还起作用</strong>。这时候可以用到上面<strong>0x02</strong>提到的<code class="language-plaintext highlighter-rouge">goneMargin</code>属性。</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>

    <span class="nt">&lt;Button</span>
        <span class="na">android:id=</span><span class="s">"@+id/buttonA"</span>
        <span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
        <span class="na">android:text=</span><span class="s">"button A"</span>
        <span class="na">android:visibility=</span><span class="s">"gone"</span>
        <span class="na">app:layout_constraintLeft_toLeftOf=</span><span class="s">"parent"</span> <span class="nt">/&gt;</span>
	<span class="c">&lt;!--当A控件设置Gone之后，希望B控件的左边距为0dp，那么可以设置layout_goneMarginLeft属性--&gt;</span>
    <span class="nt">&lt;Button</span>
        <span class="na">android:id=</span><span class="s">"@+id/buttonB"</span>
        <span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
        <span class="na">android:layout_margin=</span><span class="s">"30dp"</span>
        <span class="na">android:text=</span><span class="s">"button B"</span>
        <span class="na">app:layout_goneMarginLeft=</span><span class="s">"0dp"</span>
        <span class="na">app:layout_constraintLeft_toRightOf=</span><span class="s">"@+id/buttonA"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<h4 id="0x06-尺寸约束dimensions-constraints">0x06 尺寸约束(Dimensions constraints)</h4>

<p><strong>设置最小或最大尺寸</strong></p>

<p>可以使用以下属性：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">android:minWidth</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:minHeight</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:maxWidth</code></li>
  <li><code class="language-plaintext highlighter-rouge">android:maxHeight</code></li>
</ul>

<p>当<code class="language-plaintext highlighter-rouge">ConstraintLayout</code>宽高设置为<code class="language-plaintext highlighter-rouge">wrap_content</code>时，以上属性可以起作用。</p>

<p><strong>设置百分比布局</strong></p>

<p>当<code class="language-plaintext highlighter-rouge">ConstraintLayout</code> 子布局的宽或高设置为0dp时，可以对宽或高设置百分比，例如设置一个按钮的宽是屏幕宽度的30%，那么可以这样处理：</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>
    <span class="c">&lt;!--按钮width属性设置为0dp,然后需要指定layout_constraintWidth_default，以及layout_constraintWidth_percent两个属性--&gt;</span>
    <span class="nt">&lt;Button</span>
        <span class="na">android:id=</span><span class="s">"@+id/buttonB"</span>
        <span class="na">android:layout_width=</span><span class="s">"0dp"</span>
        <span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
        <span class="na">android:text=</span><span class="s">"button B"</span>
        <span class="na">app:layout_constraintLeft_toLeftOf=</span><span class="s">"parent"</span>
        <span class="na">app:layout_constraintWidth_default=</span><span class="s">"percent"</span>
        <span class="na">app:layout_constraintWidth_percent=</span><span class="s">"0.3"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<p>设置宽度百分比布局：</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">layout_width</code>或者<code class="language-plaintext highlighter-rouge">layout_height</code> 设置为0dp</li>
  <li>设置<code class="language-plaintext highlighter-rouge">layout_constraintWidth_default="percent"</code>或者<code class="language-plaintext highlighter-rouge">layout_constraintHeight_default="percent"</code></li>
  <li>通过<code class="language-plaintext highlighter-rouge">layout_constraintWidth_percent</code>或者<code class="language-plaintext highlighter-rouge">layout_constraintHeight_percent</code>指定百分比</li>
</ol>

<p><strong>设置宽高比例</strong></p>

<p>当<code class="language-plaintext highlighter-rouge">layout_width</code>或者<code class="language-plaintext highlighter-rouge">layout_height</code>设置为0dp时，还可以通过<code class="language-plaintext highlighter-rouge">layout_constraintDimensionRatio</code>设置宽高比例。该比例表示<code class="language-plaintext highlighter-rouge">width:height</code>的值。</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Button</span> 
	<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
	<span class="na">android:layout_height=</span><span class="s">"0dp"</span>
	<span class="na">app:layout_constraintDimensionRatio=</span><span class="s">"1:1"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>当<code class="language-plaintext highlighter-rouge">layout_width</code>与<code class="language-plaintext highlighter-rouge">layout_height</code>都设置为0dp时，通过<code class="language-plaintext highlighter-rouge">app:layout_constraintDimensionRatio</code> 指定宽高的比例。这时控件的宽高将按照该比例相对于父布局的大小设置宽高。</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;android.support.constraint.ConstraintLayout</span> <span class="err">...</span><span class="nt">&gt;</span>
    <span class="nt">&lt;Button</span>
        <span class="na">android:layout_width=</span><span class="s">"0dp"</span>
        <span class="na">android:layout_height=</span><span class="s">"0dp"</span>
        <span class="na">app:layout_constraintBottom_toBottomOf=</span><span class="s">"parent"</span>
        <span class="na">app:layout_constraintDimensionRatio=</span><span class="s">"h,16:9"</span>
        <span class="na">app:layout_constraintTop_toTopOf=</span><span class="s">"parent"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/android.support.constraint.ConstraintLayout&gt;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">h,16:9</code>的含义是<code class="language-plaintext highlighter-rouge">h:w=16:9</code> 也可设置<code class="language-plaintext highlighter-rouge">w,9:16</code>效果是一样的。</p>

<p><img src="../images/constraint-ratio.png" alt="" /></p>

<h4 id="0x07-chains">0x07 Chains</h4>

<p>在横轴或或者数轴上的控件相互约束时，可以组成一个<strong>链式约束</strong>。</p>

<p><img src="../images/constraint-chain.png" alt="" /></p>

<p>图9中，A控件与B控件相互约束，这就是一个简单的链式约束。</p>

<p><strong>链头</strong></p>

<p><img src="../images/constraint-chain-head.png" alt="" /></p>

<p><strong>Chain Style</strong></p>

<p>可以通过<code class="language-plaintext highlighter-rouge">layout_constraintHorizontal_chainStyle</code>或<code class="language-plaintext highlighter-rouge">layout_constraintVertical_chainStyle</code>设置链式控件的样式。这个属性有点像<code class="language-plaintext highlighter-rouge">LinearLayout</code>中的<code class="language-plaintext highlighter-rouge">weight</code> 属性平分布局。</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">CHAIN_SPREAD</code></li>
  <li>Weighted chain</li>
  <li><code class="language-plaintext highlighter-rouge">CHAIN_SPREAD_INSIDE</code></li>
  <li><code class="language-plaintext highlighter-rouge">CHAIN_PACKED</code></li>
</ul>

<p><img src="../images/constraint-chain-style.png" alt="" /></p>

<p><strong>设置权重</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintHorizontal_weight</code></li>
  <li><code class="language-plaintext highlighter-rouge">layout_constraintVertical_weight</code></li>
</ul>

<h4 id="0x08-引用">0x08 引用</h4>

<p>https://developer.android.com/reference/android/support/constraint/ConstraintLayout</p>]]></content><author><name>hylinux</name></author><category term="Android" /><summary type="html"><![CDATA[是时候要用ConstraintLayout替换RelativeLayout了]]></summary></entry><entry><title type="html">如何使用WorkManager执行后台任务(下)</title><link href="https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1(%E4%B8%8B)" rel="alternate" type="text/html" title="如何使用WorkManager执行后台任务(下)" /><published>2018-10-01T00:00:00+00:00</published><updated>2018-10-01T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1(%E4%B8%8B)</id><content type="html" xml:base="https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1(%E4%B8%8B)"><![CDATA[<h4 id="0x00-workmanager的高级用法">0x00 WorkManager的高级用法</h4>

<p>在上一文中已经了解到<code class="language-plaintext highlighter-rouge">WorkManager</code>的基本用法之后，今天来看看它的一些高级用法：</p>

<ul>
  <li>链式任务调用</li>
  <li>唯一任务序列</li>
  <li>传递参数和获取返回值</li>
</ul>

<h4 id="0x01-链式任务chained-tasks">0x01 链式任务(Chained tasks)</h4>

<p><code class="language-plaintext highlighter-rouge">WorkManager</code>在执行多个工作任务的时候，可以指定执行顺序。假设一个应用程序中有3个<code class="language-plaintext highlighter-rouge">OneTimeWorkRequest</code>对象：<code class="language-plaintext highlighter-rouge">workA</code>、<code class="language-plaintext highlighter-rouge">workB</code>、<code class="language-plaintext highlighter-rouge">workC</code>。这几个任务需要按照顺序执行，那么可以使用<code class="language-plaintext highlighter-rouge">WorkManager.beginWith()</code>方法加入<code class="language-plaintext highlighter-rouge">workA</code>，这时候会返回一个<code class="language-plaintext highlighter-rouge">WorkContinuation</code>对象，它定义了工作任务的执行序列。然后通过它再调用<code class="language-plaintext highlighter-rouge">WorkContinuation.then()</code>把<code class="language-plaintext highlighter-rouge">workB</code>和<code class="language-plaintext highlighter-rouge">workC</code>加入到执行队列中，最后执行<code class="language-plaintext highlighter-rouge">WorkManager.enqueue()</code>方法。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">beginWith</span><span class="p">(</span><span class="n">workA</span><span class="p">)</span>
        <span class="c1">// Note: WorkManager.beginWith() returns a</span>
        <span class="c1">// WorkContinuation object; the following calls are</span>
        <span class="c1">// to WorkContinuation methods</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workB</span><span class="p">)</span>    <span class="c1">// FYI, then() returns a new WorkContinuation instance</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workC</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">enqueue</span><span class="p">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">WorkManager</code>会按照指定的顺序来执行<code class="language-plaintext highlighter-rouge">workA</code>、<code class="language-plaintext highlighter-rouge">workB</code>、<code class="language-plaintext highlighter-rouge">workC</code>。如果其中有一个工作任务执行的时候返回<code class="language-plaintext highlighter-rouge">Worker.Result.FAILURE</code>，那么整个执行序列就会停止。</p>

<p><code class="language-plaintext highlighter-rouge">WorkManager.beginWith()</code>方法可以传递多个<code class="language-plaintext highlighter-rouge">Worker</code>对象，表示可以<strong>并行执行</strong>的任务组，然后再调用<code class="language-plaintext highlighter-rouge">then()</code>方法。当这并行任务组都执行完之后才会执行接下来<code class="language-plaintext highlighter-rouge">then</code>中的工作任务。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">()</span>
    <span class="c1">// 首先，并行执行workA1,workA2,workA3这三个任务</span>
    <span class="p">.</span><span class="nf">beginWith</span><span class="p">(</span><span class="n">workA1</span><span class="p">,</span> <span class="n">workA2</span><span class="p">,</span> <span class="n">workA3</span><span class="p">)</span>
    <span class="c1">// 当三个任务都完成之后, 开始执行workB:</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workB</span><span class="p">)</span>
    <span class="c1">// 最后再并行执行 workC1,workC2</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workC1</span><span class="p">,</span> <span class="n">workC2</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">enqueue</span><span class="p">()</span>
</code></pre></div></div>

<p>还可以通过<code class="language-plaintext highlighter-rouge">WorkContinuation.combine()</code>方法创建更加复杂的链接任务调用序列。它可以将两个<code class="language-plaintext highlighter-rouge">WorkContinuation</code>对象合并，假设要调用如下的任务序列：</p>

<p><img src="../images/workcontinuation.png" alt="" /></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">chain1</span> <span class="p">=</span> <span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">beginWith</span><span class="p">(</span><span class="n">workA</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workB</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">chain2</span> <span class="p">=</span> <span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">beginWith</span><span class="p">(</span><span class="n">workC</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workD</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">chain3</span> <span class="p">=</span> <span class="nc">WorkContinuation</span>
    <span class="p">.</span><span class="nf">combine</span><span class="p">(</span><span class="n">chain1</span><span class="p">,</span> <span class="n">chain2</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="n">workE</span><span class="p">)</span>
<span class="n">chain3</span><span class="p">.</span><span class="nf">enqueue</span><span class="p">()</span>
</code></pre></div></div>

<p>这个链式执行顺序是：子链 A-&gt;B 与子链 C-&gt;D 并行执行的，<code class="language-plaintext highlighter-rouge">workA</code>执行后再执行<code class="language-plaintext highlighter-rouge">workB</code>，以及<code class="language-plaintext highlighter-rouge">workC</code>执行后执行<code class="language-plaintext highlighter-rouge">workD</code> ；然后等待 <code class="language-plaintext highlighter-rouge">workB</code>以及<code class="language-plaintext highlighter-rouge">workD</code>都执行完，最后执行<code class="language-plaintext highlighter-rouge">workE</code>。</p>

<p>需要注意的是，<code class="language-plaintext highlighter-rouge">WorkManager</code>无法保证两个子链的执行顺序，有可能<code class="language-plaintext highlighter-rouge">chain1</code>比<code class="language-plaintext highlighter-rouge">chain2</code>快，也有可能是<code class="language-plaintext highlighter-rouge">chain1</code>比<code class="language-plaintext highlighter-rouge">chain2</code>慢。</p>

<h4 id="0x02-唯一任务序列unique-work-sequences">0x02 唯一任务序列(Unique work sequences)</h4>

<p>在应用程序开发中，可能会多次把同一个链式任务添加到<code class="language-plaintext highlighter-rouge">WorkManager</code>中，但希望只有一个链式任务在执行，这时候可以使用唯一任务序列对链式任务指定处理规则。假设，做一个下载文件操作，对一个文件下载链接，我们不需要重复下载，只需要添加一次，后面再添加这个任务，就忽略掉，因为我们不希望重复多次下载同一个文件。所以当添加两个同样名称为”download”操作任务时，对于唯一任务序列来说，可以通过<code class="language-plaintext highlighter-rouge">ExistingWorkPolicy</code>中的<code class="language-plaintext highlighter-rouge">REPLACE</code>，<code class="language-plaintext highlighter-rouge"> KEEP</code> 和 <code class="language-plaintext highlighter-rouge">APPEND</code>，来指定添加的策略。</p>

<ul>
  <li>REPLACE：新任务将替换旧的</li>
  <li>KEEP：新任务会被丢弃，旧的任务会被保持</li>
  <li>APPEND：追加，旧任务执行之后再执行新的任务。</li>
</ul>

<p>使用<code class="language-plaintext highlighter-rouge">beginUniqueWork()</code>方法可以创建任务序列，并且可以指定唯一的一个名称(name)。然后再<code class="language-plaintext highlighter-rouge">ExistingWorkPolicy</code>指定任务的替换策略</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">WorkContinuation</span> <span class="n">continuation</span> <span class="p">=</span> <span class="n">mWorkManager</span>
                <span class="p">.</span><span class="nf">beginUniqueWork</span><span class="p">(</span><span class="s">"download"</span><span class="p">,</span>
                       <span class="nc">ExistingWorkPolicy</span><span class="p">.</span><span class="nc">KEEP</span><span class="p">,</span>
                       <span class="nc">OneTimeWorkRequest</span><span class="p">.</span><span class="nf">from</span><span class="p">(</span><span class="nc">CleanupWorker</span><span class="p">))</span>
</code></pre></div></div>

<h4 id="0x03-传递参数和获取返回值">0x03 传递参数和获取返回值</h4>

<p>任务执行可以传递参数以及获取到任务执行的结果。使用<code class="language-plaintext highlighter-rouge">WorkRequst.Builder.setInputData()</code>方法传递一个<code class="language-plaintext highlighter-rouge">Data</code>对象，它是key-value形式的对象，使用<code class="language-plaintext highlighter-rouge">Data.Builder</code>来创建。在<code class="language-plaintext highlighter-rouge">Worker</code>类中可以使用<code class="language-plaintext highlighter-rouge">Worker.getInputData()</code>获取到参数。</p>

<p>同样地，在<code class="language-plaintext highlighter-rouge">Worker</code>中可以使用<code class="language-plaintext highlighter-rouge">Worker.setOutputData()</code>设置一个<code class="language-plaintext highlighter-rouge">Data</code>对象的返回值。要获取到这个返回值就通过<code class="language-plaintext highlighter-rouge">LiveData&lt;WorkStatus&gt;</code>。</p>

<p>举个栗子：</p>

<p>有一个下载任务，在<code class="language-plaintext highlighter-rouge">Worker</code>中获取传递过来的参数url，然后执行下载，最后设置下载结果。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// the result key:</span>
<span class="k">const</span> <span class="kd">val</span> <span class="py">KEY_RESULT</span> <span class="p">=</span> <span class="s">"result"</span>
<span class="kd">class</span> <span class="nc">DownloadWorker</span><span class="p">(</span><span class="n">context</span> <span class="p">:</span> <span class="nc">Context</span><span class="p">,</span> <span class="n">params</span> <span class="p">:</span> <span class="nc">WorkerParameters</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Worker</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>  <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">doWork</span><span class="p">():</span> <span class="nc">Result</span> <span class="p">{</span>
        <span class="c1">// 获取参数</span>
        <span class="kd">val</span> <span class="py">url</span> <span class="p">=</span> <span class="nf">getInputData</span><span class="p">(</span><span class="s">"url"</span><span class="p">)</span>
        <span class="c1">// 执行下载</span>
        <span class="kd">val</span> <span class="py">result</span> <span class="p">=</span> <span class="nf">download</span><span class="p">(</span><span class="n">url</span><span class="p">);</span>
        <span class="c1">// 设置下载结果</span>
        <span class="kd">val</span> <span class="py">output</span><span class="p">:</span> <span class="nc">Data</span> <span class="p">=</span> <span class="nf">mapOf</span><span class="p">(</span><span class="nc">KEY_RESULT</span> <span class="n">to</span> <span class="n">result</span><span class="p">).</span><span class="nf">toWorkData</span><span class="p">()</span>
        <span class="nf">setOutputData</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
        <span class="c1">// 任务执行成功</span>
        <span class="k">return</span> <span class="nc">Result</span><span class="p">.</span><span class="nc">SUCCESS</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>然后，通过<code class="language-plaintext highlighter-rouge">WorkRequest</code>传递参数</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 构造下载链接参数</span>
<span class="kd">val</span> <span class="py">urlData</span><span class="p">:</span> <span class="nc">Data</span> <span class="p">=</span> <span class="nf">mapOf</span><span class="p">(</span><span class="s">"url"</span> <span class="n">to</span> <span class="s">"https://developer.android.com/images/topic/libraries/architecture/workmanager-chain.svg"</span><span class="p">)</span>
                     <span class="p">.</span><span class="nf">toWorkData</span><span class="p">()</span>
<span class="c1">// 构造WorkRequest并传递下载参数</span>
<span class="kd">val</span> <span class="py">downloadWork</span> <span class="p">=</span> <span class="nc">OneTimeWorkRequest</span><span class="p">.</span><span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">DownloadWorker</span><span class="p">&gt;()</span>
        <span class="p">.</span><span class="nf">setInputData</span><span class="p">(</span><span class="n">urlData</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">build</span><span class="p">()</span>
<span class="c1">// 交给WorkManager执行任务</span>
<span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">enqueue</span><span class="p">(</span><span class="n">downloadWork</span><span class="p">)</span>
</code></pre></div></div>

<p>最后，通过<code class="language-plaintext highlighter-rouge">WorkStatus</code>获取返回值</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">getStatusById</span><span class="p">(</span><span class="n">downloadWork</span><span class="p">.</span><span class="n">id</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">observe</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nc">Observer</span> <span class="p">{</span> <span class="n">status</span> <span class="p">-&gt;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">status</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">isFinished</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">val</span> <span class="py">myResult</span> <span class="p">=</span> <span class="n">status</span><span class="p">.</span><span class="n">outputData</span><span class="p">.</span><span class="nf">getString</span><span class="p">(</span><span class="nc">KEY_RESULT</span><span class="p">,</span>
                      <span class="n">myDefaultValue</span><span class="p">)</span>
                <span class="c1">// ... do something with the result ...</span>
            <span class="p">}</span>
        <span class="p">})</span>
</code></pre></div></div>

<h4 id="0x04-引用">0x04 引用</h4>

<p>https://developer.android.com/topic/libraries/architecture/workmanager/advanced</p>

<p>https://developer.android.com/reference/androidx/work/ExistingWorkPolicy</p>

<p>http://clmirror.storage.googleapis.com/codelabs/android-workmanager/index.html?index=..%2F..%2Findex#0</p>]]></content><author><name>hylinux</name></author><category term="Android" /><summary type="html"><![CDATA[后台任务处理指南]]></summary></entry><entry><title type="html">如何使用WorkManager执行后台任务(上)</title><link href="https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1-%E4%B8%8A" rel="alternate" type="text/html" title="如何使用WorkManager执行后台任务(上)" /><published>2018-09-29T00:00:00+00:00</published><updated>2018-09-29T00:00:00+00:00</updated><id>https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%EF%BC%88%E4%B8%8A%EF%BC%89</id><content type="html" xml:base="https://hylinux1024.github.io/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8WorkManager%E6%89%A7%E8%A1%8C%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1-%E4%B8%8A"><![CDATA[<h4 id="0x00-简述">0x00 简述</h4>

<p>WorkManager 是 Android Jetpack中的一部分，它主要是封装了 Android 后台任务的调度逻辑。在前文<a href="https://wecode.xyz/Android%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%84%E7%90%86%E6%8C%87%E5%8D%97">《Android后台任务处理指南》</a>一文中知道，WorkManager 是高级 API，它实际是封装了<a href="https://developer.android.com/reference/android/app/job/JobScheduler.html"><code class="language-plaintext highlighter-rouge">JobScheduler</code></a>, <a href="https://github.com/firebase/firebase-jobdispatcher-android#user-content-firebase-jobdispatcher-">Firebase JobDispatcher</a>, 和 <a href="https://developer.android.com/reference/android/app/AlarmManager.html"><code class="language-plaintext highlighter-rouge">AlarmManager</code></a> 底层的使用，提供了简单且灵活易用的API，它有很多优势：</p>

<ul>
  <li>支持异步一次性或周期任务</li>
  <li>支持网络、存储空间以及电量状态等约束</li>
  <li>可使用链式的调用方式来执行任务，也包括并行任务处理</li>
  <li>一个工作任务的执行结果可以作为下一个任务的输入</li>
  <li>兼容API 14 以上</li>
  <li>可以支持Google play services</li>
  <li>支持LiveData</li>
</ul>

<h4 id="0x01-理论篇重要的类">0x01 理论篇——重要的类</h4>

<p>WorkManager库中有一些非常重要的类，这些类帮助你构建后台执行的工作任务：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Worker</code>：这是一个抽象类，它表示一个工作任务，继承这个类实现<code class="language-plaintext highlighter-rouge">doWork()</code>方法，这里就是实现执行任务的主要逻辑。</li>
  <li><code class="language-plaintext highlighter-rouge">WorkRequest</code>：表示一个工作任务请求，指定执行哪个<code class="language-plaintext highlighter-rouge">Work</code>，它还可以设置任务执行的约束条件。每一个<code class="language-plaintext highlighter-rouge">WorkRequest</code>都有一个自动生成的唯一ID。使用这个ID可以取消任务的执行，或者取得当前任务的执行状态。<code class="language-plaintext highlighter-rouge">WorkRequest</code>也是一个抽象类，使用的时候是框架库中提供的子类<code class="language-plaintext highlighter-rouge">OneTimeWorkRequest</code>和<code class="language-plaintext highlighter-rouge">PeriodicWorkRequest</code>类，分别代表<strong>一次性任务和周期任务</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">WorkRequest.Builder</code>：这是一个创建工作任务请求的辅助工具类。它有两个具体的实现：<code class="language-plaintext highlighter-rouge">OneTimeWorkRequest.Builder</code>类和<code class="language-plaintext highlighter-rouge">PeriodicWorkRequest.Builder</code>类，可以分别创建相应的任务请求实例。</li>
      <li><code class="language-plaintext highlighter-rouge">Constraints</code>：它表示工作任务执行的约束条件。指的是在哪一些系统条件下（例如只有网络连接情况下），这个工作任务会被执行。同样地，创建<code class="language-plaintext highlighter-rouge">Constraints</code>也有<code class="language-plaintext highlighter-rouge">Builder</code>方法：<code class="language-plaintext highlighter-rouge">Constraints.Builder</code>。约束类是可以在使用<code class="language-plaintext highlighter-rouge">WorkRequest.Builder</code> 创建的时候进行传递给<code class="language-plaintext highlighter-rouge">WorkRequest</code>。</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">WorkManager</code>：工作任务管理器，它是用于管理工作任务的请求队列的类。通过它可以把一个<code class="language-plaintext highlighter-rouge">WorkRequest</code>加入到任务队列中，然后根据系统的资源和约束条件对工作任务进行调度。</li>
  <li><code class="language-plaintext highlighter-rouge">WorkStatus</code>：工作状态类，它包含了工作任务当前的状态信息。<code class="language-plaintext highlighter-rouge">WorkManager</code> 为每一个<code class="language-plaintext highlighter-rouge">WorkRequest</code>都提供了<code class="language-plaintext highlighter-rouge">LiveData</code>对象，而<code class="language-plaintext highlighter-rouge">LiveData</code>对象又持有<code class="language-plaintext highlighter-rouge">WorkStatus</code>信息，因此通过<code class="language-plaintext highlighter-rouge">LiveData</code>可以监听到当前任务的状态信息，并且获取到任务结束后的执行结果。</li>
</ul>

<p>通过上面的描述，可以简单的画出下面的关系图：</p>

<p><img src="../images/work-manager-relation.png" alt="" /></p>

<h4 id="0x02-实践篇工作流">0x02 实践篇——工作流</h4>

<p>假设我们在开发一个图片相关的APP，这个应用需要期地压缩它存储的图片。我们使用<code class="language-plaintext highlighter-rouge">WorkManager</code>来实现这个需求。这种情况，我们不关心压缩任务什么时候开始，只要开启一个压缩任务，其他的就交给<code class="language-plaintext highlighter-rouge">WorkManager</code>了。</p>

<ol>
  <li><strong>定义Worker</strong></li>
</ol>

<p>我们首先要定义一个<code class="language-plaintext highlighter-rouge">Worker</code>类，并重写<code class="language-plaintext highlighter-rouge">doWork()</code>方法，这里定义了如何执行任务的具体逻辑。例如在此例子中，<code class="language-plaintext highlighter-rouge">myCompress()</code>就是具体的执行任务的逻辑。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">CompressWorker</span><span class="p">(</span><span class="n">context</span> <span class="p">:</span> <span class="nc">Context</span><span class="p">,</span> <span class="n">params</span> <span class="p">:</span> <span class="nc">WorkerParameters</span><span class="p">)</span>
    <span class="p">:</span> <span class="nc">Worker</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">doWork</span><span class="p">():</span> <span class="nc">Result</span> <span class="p">{</span>
        <span class="c1">// Do the work here--in this case, compress the stored images.</span>
        <span class="c1">// In this example no parameters are passed; the task is</span>
        <span class="c1">// assumed to be "compress the whole library."</span>
        <span class="nf">myCompress</span><span class="p">()</span>

        <span class="c1">// Indicate success or failure with your return value:</span>
        <span class="k">return</span> <span class="nc">Result</span><span class="p">.</span><span class="nc">SUCCESS</span>

        <span class="c1">// (Returning RETRY tells WorkManager to try this task again</span>
        <span class="c1">// later; FAILURE says not to try again.)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">doWork()</code> 执行后还可以返回执行的结果：<code class="language-plaintext highlighter-rouge">Result.SUCCESS</code>、<code class="language-plaintext highlighter-rouge">Result.FAILURE</code>和<code class="language-plaintext highlighter-rouge">Result.RETRY</code> 分别表示<strong>成功</strong>、<strong>失败</strong>、<strong>重试</strong>。</p>

<ol>
  <li><strong>创建WorkRequest</strong></li>
</ol>

<p>使用<code class="language-plaintext highlighter-rouge">WorkRequest</code>的子类，根据具体业务创建对应的请求实例。例如本例中，使用一次性任务来构建请求。然后把任务请求加入到<code class="language-plaintext highlighter-rouge">WorkManager</code>管理的队列中。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">compressionWork</span> <span class="p">=</span> <span class="nc">OneTimeWorkRequest</span><span class="p">.</span><span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">CompressWorker</span><span class="p">&gt;().</span><span class="nf">build</span><span class="p">()</span>
<span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">enqueue</span><span class="p">(</span><span class="n">compressionWork</span><span class="p">)</span>
</code></pre></div></div>

<p>这样这个任务就交给<code class="language-plaintext highlighter-rouge">WorkManager</code>了，任务管理者根据当前系统情况（是否充电、网络状态等等）对任务进行调度。如果没有给<code class="language-plaintext highlighter-rouge">WorkRequest</code>指定约束条件，那么这个任务会马上执行。如果需要得到这个任务的状态，可以通过<code class="language-plaintext highlighter-rouge">LiveData&lt;WorkStatus&gt;</code>来监听。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">getStatusById</span><span class="p">(</span><span class="n">compressionWork</span><span class="p">.</span><span class="n">id</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">observe</span><span class="p">(</span><span class="n">lifecycleOwner</span><span class="p">,</span> <span class="nc">Observer</span> <span class="p">{</span> <span class="n">workStatus</span> <span class="p">-&gt;</span>
                    <span class="c1">// Do something with the status</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">workStatus</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">workStatus</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">isFinished</span><span class="p">)</span> <span class="p">{</span>
                        <span class="c1">// ...</span>
                    <span class="p">}</span>
                <span class="p">})</span>
</code></pre></div></div>

<ol>
  <li><strong>任务约束</strong></li>
</ol>

<p>可以给一个任务指定约束条件。例如，可以在设备空闲的时候、或者充电的时候执行任务。这种情况，可以使用<code class="language-plaintext highlighter-rouge">Constraints.Builder</code>来创建一个约束条件实例，然后传递到<code class="language-plaintext highlighter-rouge">WorkRequest</code>中。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Create a Constraints object that defines when the task should run</span>
<span class="kd">val</span> <span class="py">myConstraints</span> <span class="p">=</span> <span class="nc">Constraints</span><span class="p">.</span><span class="nc">Builder</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">setRequiresDeviceIdle</span><span class="p">(</span><span class="k">true</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">setRequiresCharging</span><span class="p">(</span><span class="k">true</span><span class="p">)</span>
        <span class="c1">// Many other constraints are available, see the</span>
        <span class="c1">// Constraints.Builder reference</span>
        <span class="p">.</span><span class="nf">build</span><span class="p">()</span>

<span class="c1">// ...then create a OneTimeWorkRequest that uses those constraints</span>
<span class="kd">val</span> <span class="py">compressionWork</span> <span class="p">=</span> <span class="nc">OneTimeWorkRequestBuilder</span><span class="p">&lt;</span><span class="nc">CompressWorker</span><span class="p">&gt;()</span>
        <span class="p">.</span><span class="nf">setConstraints</span><span class="p">(</span><span class="n">myConstraints</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">build</span><span class="p">()</span>
</code></pre></div></div>

<p>最后，还是调用<code class="language-plaintext highlighter-rouge">WorkManager</code>的<code class="language-plaintext highlighter-rouge">enqueue</code>方法将<code class="language-plaintext highlighter-rouge">WorkRequest</code>加入队列。这时候，<code class="language-plaintext highlighter-rouge">WorkManager</code>在执行任务的时候就会考虑约束条件的情况。</p>

<ol>
  <li><strong>取消任务</strong></li>
</ol>

<p>取消任务需要一个任务ID，可以通过<code class="language-plaintext highlighter-rouge">WorkRequest</code>来获取。然后调用<code class="language-plaintext highlighter-rouge">WorkManager</code>的<code class="language-plaintext highlighter-rouge">cancelWorkById</code>方法来取消任务的执行。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">compressionWorkId</span><span class="p">:</span><span class="nc">UUID</span> <span class="p">=</span> <span class="n">compressionWork</span><span class="p">.</span><span class="nf">getId</span><span class="p">()</span>
<span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">cancelWorkById</span><span class="p">(</span><span class="n">compressionWorkId</span><span class="p">)</span>
</code></pre></div></div>

<ol>
  <li><strong>给任务设置Tag</strong></li>
</ol>

<p>可以给一组任务设置一个Tag。在创建 <code class="language-plaintext highlighter-rouge">WorkRequest</code> 的时候，可以执行给它指定一个Tag；而另一个任务请求也可以设置相同的Tag。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">cacheCleanupTask</span> <span class="p">=</span>
        <span class="nc">OneTimeWorkRequest</span><span class="p">.</span><span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">MyCacheCleanupWorker</span><span class="p">&gt;()</span>
    <span class="p">.</span><span class="nf">addTag</span><span class="p">(</span><span class="s">"one-tag"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">build</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">loadImageTask</span> <span class="p">=</span> <span class="nc">OneTimeWorkRequest</span><span class="p">.</span><span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">MyImageLoadWorker</span><span class="p">&gt;()</span>
    <span class="p">.</span><span class="nf">addTag</span><span class="p">(</span><span class="s">"one-tag"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">build</span><span class="p">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">WorkManager</code>提供了很多有用的API操作这些相同Tag的任务。例如，<code class="language-plaintext highlighter-rouge">WorkManager.cancelAllWorkByTag()</code> 可以取消具有相同Tag的一组任务；还可以通过<code class="language-plaintext highlighter-rouge">WorkManager.getStatusesByTag()</code>获取一组任务的状态列表。</p>

<ol>
  <li><strong>周期任务</strong></li>
</ol>

<p>有些业务场景是需要重复执行一个任务的。例如，在一个图片应用中，可能会有一个定期检查需要压缩图片的任务。这时候可使用到 <code class="language-plaintext highlighter-rouge">PeriodicWorkRequest.Builder</code> 来创建一个周期任务。跟一次性任务一样，通过<code class="language-plaintext highlighter-rouge">WorkManager.enqueue()</code> 方法加入到工作任务队列中。</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">photoCheckBuilder</span> <span class="p">=</span>
        <span class="nc">PeriodicWorkRequest</span><span class="p">.</span><span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">PhotoCheckWorker</span><span class="p">&gt;(</span><span class="mi">12</span><span class="p">,</span> <span class="nc">TimeUnit</span><span class="p">.</span><span class="nc">HOURS</span><span class="p">)</span>
<span class="c1">// ...if you want, you can apply constraints to the builder here...</span>

<span class="c1">// Create the actual work object:</span>
<span class="kd">val</span> <span class="py">photoCheckWork</span> <span class="p">=</span> <span class="n">photoCheckBuilder</span><span class="p">.</span><span class="nf">build</span><span class="p">()</span>
<span class="c1">// Then enqueue the recurring task:</span>
<span class="nc">WorkManager</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">enqueue</span><span class="p">(</span><span class="n">photoCheckWork</span><span class="p">)</span>
</code></pre></div></div>

<p>接下来的事情就交给 <code class="language-plaintext highlighter-rouge">WorkManager</code> 了。</p>

<h4 id="0x03-引用">0x03 引用</h4>

<p>https://developer.android.com/topic/libraries/architecture/workmanager/basics</p>]]></content><author><name>hylinux</name></author><category term="Android" /><summary type="html"><![CDATA[后台任务处理指南]]></summary></entry><entry><title type="html">Android 后台任务处理指南</title><link href="https://hylinux1024.github.io/Android%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%84%E7%90%86%E6%8C%87%E5%8D%97" rel="alternate" type="text/html" title="Android 后台任务处理指南" /><published>2018-09-28T00:00:00+00:00</published><updated>2018-09-28T00:00:00+00:00</updated><id>https://hylinux1024.github.io/Android%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%84%E7%90%86%E6%8C%87%E5%8D%97</id><content type="html" xml:base="https://hylinux1024.github.io/Android%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%A4%84%E7%90%86%E6%8C%87%E5%8D%97"><![CDATA[<h4 id="0x00-后台任务处理指南">0x00 后台任务处理指南</h4>

<p>Android的主线程（MainThread）主要是处理UI和绘制，接收用户的交互以及处理生命周期等事件。在UI线程中处理耗时操作，将严重影响用户的体验。因此很多任务是需要放在后台任务中进行处理的。</p>

<p>当需要后台任务时需要考虑以下因素：</p>

<ul>
  <li><strong>任务是否可以延期执行，还是需要马上执行？</strong> 例如，当你点击页面上按钮去请求网络数据时，这个任务是需要立即执行，不应该让用户等待。如果是上传用户日志到服务器中，这个任务在不影响用户体验的情况下是可以推迟执行的。</li>
  <li><strong>任务一旦开启，操作系统是否要保持该任务所在的进程？</strong> 例如，解码一个<code class="language-plaintext highlighter-rouge">bitmap</code>并显示到屏幕上，这个任务是需要<code class="language-plaintext highlighter-rouge">App</code>在前台并且该进程是活跃的。而一个音乐播放器在<code class="language-plaintext highlighter-rouge">App</code>退到后台甚至用户没有进行操作时，也是需要保持播放服务能够正常的进行。</li>
  <li><strong>任务是否需要响应系统的触发事件？</strong>这些触发事件是指像网络、电量、空间存储等状态的变化。当这些状态变化的时候，可以触发任务的执行。例如当从飞行模式切换回来时，<code class="language-plaintext highlighter-rouge">App</code>能够自动与服务端通讯，而且如果此时进程已经被系统杀掉，那么该任务所在的进程应该在有网络的情况下重新创建，然后执行后台任务。</li>
</ul>

<p>目前Android系统中处理后台任务的方式有以下几种：</p>

<ul>
  <li>ThreadPools（线程池）</li>
  <li>Foreground Services（前台服务）</li>
  <li>WorkManager（任务管理器）</li>
</ul>

<h5 id="threadpools">ThreadPools</h5>

<p>当一个任务必须在<code class="language-plaintext highlighter-rouge">App</code>处于前台状态时处理，可以使用<code class="language-plaintext highlighter-rouge">ThreadPools</code>。如果要响应操作系统的触发事件状态，那么只能通过<code class="language-plaintext highlighter-rouge">Broadcast Receiver</code>了。</p>

<h5 id="foregound-services">Foregound Services</h5>

<p>对于必须执行完成的，并需要马上执行的任务，可以使用<code class="language-plaintext highlighter-rouge">Foreground Services</code>。前台服务会在通知栏中保持一个无法手动取消的通知，它会告诉操作系统这个任务非常重要，不要杀掉。</p>

<h5 id="workmanager">WorkManager</h5>

<p>对于必须执行完成的，并能否延迟执行的任务，可以使用<code class="language-plaintext highlighter-rouge">WorkManager</code>。任务管理器是一个<code class="language-plaintext highlighter-rouge">Android</code>库，它优雅地实现了可延迟任务的执行，它还能够响应操作系统的触发事件（网络状态、电量变化等事件）。Android 6.0以上操作系统 <code class="language-plaintext highlighter-rouge">WorkManager</code>使用了框架层中的<code class="language-plaintext highlighter-rouge">JobScheduler</code>，用于优化电量消耗和批量任务的执行。在低于<code class="language-plaintext highlighter-rouge">Android 6.0 (API 23) </code>的系统，如果项目有引用<code class="language-plaintext highlighter-rouge">Firebase</code>相关依赖库的话， <code class="language-plaintext highlighter-rouge">WorkManager</code> 则会使用<code class="language-plaintext highlighter-rouge">Firebase JobDispatcher</code> ，否则<code class="language-plaintext highlighter-rouge">WorkManager</code> 使用<code class="language-plaintext highlighter-rouge">AlarmManager</code>来优雅地处理后台任务。</p>

<p>因此<code class="language-plaintext highlighter-rouge">WorkManager</code> 其实是封装了<code class="language-plaintext highlighter-rouge">JobScheduler</code> 、<code class="language-plaintext highlighter-rouge">Firebase JobDispatcher</code> 和 <code class="language-plaintext highlighter-rouge">AlarmManager</code> 的调用逻辑。它能够以最优的方式选择处理后台任务的方式，这也是<code class="language-plaintext highlighter-rouge">google</code>目前推荐处理后台任务的一种方式。</p>

<h6 id="后台任务处理的限制">后台任务处理的限制</h6>

<p>为了延长电量使用，在 <code class="language-plaintext highlighter-rouge">App</code> 处于后台不可见得情况下 Android 限制了后台任务的执行情况。</p>

<ul>
  <li>Android 6.0 (API 23) 引入了 <a href="https://developer.android.com/training/monitoring-device-state/doze-standby">Doze mode and app standby</a>（低耗电模式和待机模式）。在此模式下限制了 <code class="language-plaintext highlighter-rouge">App</code>访问网络、工作任务和同步的操作。</li>
  <li>Android 7.0 (API 24) 限制了隐式广播<code class="language-plaintext highlighter-rouge">(limited implicit broadcasts)</code> 并引入了 <a href="https://developer.android.com/about/versions/nougat/android-7.0#doze_on_the_go">Doze-on-the-Go</a></li>
  <li>Android 8.0 (API 26) 深度限制了后台任务行为<a href="https://developer.android.com/about/versions/oreo/background">limited background behavior</a>，例如后台获取地理位置。</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">WorkManager</code> 是处理以上限制的最好工具。</p>

<h4 id="0x01-参考文档">0x01 参考文档</h4>

<p><a href="https://developer.android.com/guide/background">https://developer.android.com/guide/background</a></p>]]></content><author><name>hylinux</name></author><category term="Android" /><summary type="html"><![CDATA[后台任务处理指南]]></summary></entry></feed>