<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>温斯渤</title>
  
  <subtitle>To Be A Better Man.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.wensibo.top/"/>
  <updated>2019-01-13T14:40:56.732Z</updated>
  <id>http://www.wensibo.top/</id>
  
  <author>
    <name>温斯渤</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>2018年终总结</title>
    <link href="http://www.wensibo.top/2019/01/13/2018summary/"/>
    <id>http://www.wensibo.top/2019/01/13/2018summary/</id>
    <published>2019-01-13T08:07:25.000Z</published>
    <updated>2019-01-13T14:40:56.732Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>2018的年终总结被我活生生拖了这么多天,拖延症已经病入膏肓了.2018年是我工作的第一年,在这一年中我算过得比较顺利,在工作中成长了很多.这篇文章将会对去年做一个总结,并为今年做一些规划.<br><a id="more"></a> </p><h1 id="关于工作"><a href="#关于工作" class="headerlink" title="关于工作"></a>关于工作</h1><p>熟悉我的朋友应该都知道我是去年毕业的,虽然没有进入BAT，但是我非常喜欢现在的公司，我所在的团队也很优秀．原谅我不能一一向每个人道谢，总之很感激在自己工作的第一年就能够遇到大伙.在这里真的很感谢我的导师，从我入职的第一天起就很照顾我，帮我迅速熟悉业务，为我讲解遇到的问题，是他让我快速成长，知道自己有哪些方便比较薄弱并知道该如何弥补．</p><h1 id="关于技术"><a href="#关于技术" class="headerlink" title="关于技术"></a>关于技术</h1><p>工作了才知道自己以前掌握的都是一些皮毛，也才知道业界有很多先进的成熟的方案，在工作的近一年内，团队内引入了很多新的技术方案，我在其中也学到很多，以下列举几项</p><h2 id="Kotlin"><a href="#Kotlin" class="headerlink" title="Kotlin"></a>Kotlin</h2><p>关于Kotlin，我从今年下半年开始学习，前后看了《Kotlin实战》、《疯狂Kotlin讲义》以及极客时间的《快速手上Kotlin》，一开始学完之后虽然知道他的简介与威力，但却非常不适应，后面实战之后才发现，写了两天之后就再也不想用java了．我现在在项目中也已经大部分使用Kotlin进行编写了，有一些坑还没遇到，接下来我会写一个Kotlin系列的文章与大家一起分享Kotlin的魅力．</p><h2 id="组件化"><a href="#组件化" class="headerlink" title="组件化"></a>组件化</h2><p>个人开发很少会需要组件化，只有在大的商业项目中才能见其优势．组件化是这两年比较火热的方向，我们团队也完成了组件化的工作，并在内部分享了组件化方案的实现原理以及遇到的坑，我私底下也看了业界目前的几个相对比较成熟的组件化方案，对其有了较深的理解.</p><h2 id="Android碎片化适配"><a href="#Android碎片化适配" class="headerlink" title="Android碎片化适配"></a>Android碎片化适配</h2><p>做Android开发的遇到最多的问题就是碎片化了,今年是Android全面屏手机百花齐放的一年,年初的刘海屏,到年中的水滴屏,到后面的滑盖屏以及最近的挖孔屏.OEM厂商在推出新机型的同时也给我们开发者增加了适配难度,尤其是手机屏幕宽高比以及异性全面屏还有AndroidP的适配,都非常不容易,我们团队去年上半年做了一轮适配,这不现在适配方案还是不全了,后续还会继续更新适配方案.</p><h2 id="Android源码"><a href="#Android源码" class="headerlink" title="Android源码"></a>Android源码</h2><p>程序开发远远不能停留在只会用上面,更需要理解背后的原理,那么看源码则是最好的一个方法.在工作中发现自己对Framework层的一些知识不太熟悉,这也导致在平时的工作中遇到一些问题不能自己分辨出来,而是需要通过搜索引擎需求帮助,如果对源码足够熟悉,那么就可以通过堆栈错误信息很快的查找到错误根源,也是沉淀自己技术水平的最佳方式.在源码方面,今年看了两本书:《Android进阶解密》和《深入理解 Android 卷I》,啃完之后发现真的能够有效提高工作效率</p><h2 id="短视频-amp-直播"><a href="#短视频-amp-直播" class="headerlink" title="短视频&amp;直播"></a>短视频&amp;直播</h2><p>短视频和直播是这两年比较火热的技术,不过技术原理还是在底层,我这种非科班出身的学起来还是比较吃力的,虽然团队内部进行了相关主题的分享,但是要真正掌握好还是需要自身多花时间学习.</p><h1 id="关于博客"><a href="#关于博客" class="headerlink" title="关于博客"></a>关于博客</h1><p>说起博客真的很惭愧,去年总共才更新了两篇,完全没有达到要求,当然原因也是存在的.今年4月份入职,工作之后才发现很难抽时间静下心来写文章,另一方面因为在工作的时候发现自己还有很多需要学习和进步的,所以周末会花时间在学习上,利用碎片时间学习效率也就没有那么高,也就很难再抽时间写文章.这一点我希望在新的2019年能够得到改善,不要再为没有时间找借口,只要是自己想做的事情,再没有时间也会抽出来的,等到写2019年年终总结的时候希望自己不要打脸.</p><h1 id="关于旅游"><a href="#关于旅游" class="headerlink" title="关于旅游"></a>关于旅游</h1><p>今年公司的年度旅游去了日本东京和富士山,很幸运第一年入职就能去海外,日本干净的街道和富士山的美景真的很赞,大家有机会真的可以去玩一玩.<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2019-1-13Summary/Mount_Fuji.jpg" alt="富士山" title="">                </div>                <div class="image-caption">富士山</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2019-1-13Summary/tokyo_tower.jpg" alt="东京塔" title="">                </div>                <div class="image-caption">东京塔</div>            </figure></p><h1 id="关于其他"><a href="#关于其他" class="headerlink" title="关于其他"></a>关于其他</h1><p>工作快一年了发现自己的肚子越来越大了,主要是没有太注重运动,为了避免自己太早进入油腻中年的行列中,新的一年希望自己多花点时间在运动上面,也希望大家都能够注意身体!</p><p>最后祝大家新的一年心想事成,越来越棒!!!</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;2018的年终总结被我活生生拖了这么多天,拖延症已经病入膏肓了.2018年是我工作的第一年,在这一年中我算过得比较顺利,在工作中成长了很多.这篇文章将会对去年做一个总结,并为今年做一些规划.&lt;br&gt;
    
    </summary>
    
      <category term="年终总结" scheme="http://www.wensibo.top/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/"/>
    
    
      <category term="总结" scheme="http://www.wensibo.top/tags/%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Gradle（二）：深入学习Groovy</title>
    <link href="http://www.wensibo.top/2018/03/16/gradle2/"/>
    <id>http://www.wensibo.top/2018/03/16/gradle2/</id>
    <published>2018-03-16T08:32:25.000Z</published>
    <updated>2018-10-21T08:26:20.784Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><a href="http://wensibo.top/2018/02/07/gradle1/" target="_blank" rel="noopener">上一篇文章</a>我们从整体上初步认识了Gradle，也说了Gradle的学习曲线，这篇文章我们就按照顺序来讲解Groovy语言。<br>需要说明的是这篇文章大部分内容是翻译自<a href="http://www.groovy-lang.org/syntax.html" target="_blank" rel="noopener">Groovy的官方文档</a>，不过中间也有些许内容是我自己学习过程中总结整理的，如果大家英语好的话建议直接看英文文档。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2018-02-07Gradle1/gradle_logo.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><br><a id="more"></a> </p><p><strong>这是一个系列的Gradle文章：</strong></p><ul><li><a href="http://wensibo.top/2018/02/07/gradle1/" target="_blank" rel="noopener">一口一口吃掉Gradle（一）：初识Gradle</a></li><li><a href="http://wensibo.top/2018/03/16/gradle2/" target="_blank" rel="noopener">一口一口吃掉Gradle（二）：深入学习Groovy</a></li></ul><h1 id="Groovy是一门什么语言"><a href="#Groovy是一门什么语言" class="headerlink" title="Groovy是一门什么语言"></a>Groovy是一门什么语言</h1><p>在Groovy的官网上有对Groovy的一段描述：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Groovy tries to be as natural as possible for Java developers. We’ve tried to follow the principle of least surprise when designing Groovy, particularly for developers learning Groovy who’ve come from a Java background.</span><br></pre></td></tr></table></figure></p><p>大意是说Groovy就是为了JAVA程序员“量身定做”的，既然这么说的话，我们学习Groovy应该是比较容易的。当然事实也是如此，不过Groovy作为一门脚本语言，也有它与JAVA的区别之处，例如在JAVA中我们需要每一行都要分号作为结束标志，但是Groovy却可以省略；我们调用JAVA中的方法时需要使用括号，而Groovy中也可以省略。当然还有很多特点使得Groovy相比JAVA来说都是一门更简洁的语言。接下来我们就来了解一番。</p><h1 id="搭建Groovy开发环境"><a href="#搭建Groovy开发环境" class="headerlink" title="搭建Groovy开发环境"></a>搭建Groovy开发环境</h1><p>尽管Gradle使用了Groovy作为开发语言，但是确切地说Gradle使用的是基于Groovy的领域特定语言(DSL)，也就是说Gradle只是使用了Groovy中的一些语法。所以当我们来了解Groovy时最好还是搭建Groovy的开发环境。</p><h2 id="Linux环境下"><a href="#Linux环境下" class="headerlink" title="Linux环境下"></a>Linux环境下</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span># 到sdkman官网下载对应的安装shell script，然后调用bash解析器去执行</span><br><span class="line"><span class="meta">#</span># 在这个过程中最好关闭代理</span><br><span class="line">curl -s get.sdkman.io | bash</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span># 继续执行下面的命令</span><br><span class="line">source ~/.sdkman/bin/sdkman-init.sh</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span># 安装Groovy</span><br><span class="line">sdk install groovy</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span># 查看Groovy版本</span><br><span class="line">groovy -version</span><br><span class="line">Groovy Version: 2.4.14 JVM: 1.8.0_131 Vendor: Oracle Corporation OS: Linux</span><br></pre></td></tr></table></figure><p>如果能够成功显示如上的Groovy版本则说明安装成功。</p><h2 id="Windows环境下"><a href="#Windows环境下" class="headerlink" title="Windows环境下"></a>Windows环境下</h2><p>点击这个<a href="http://www.groovy-lang.org/download.html#otherways" target="_blank" rel="noopener">连接</a>到Groovy官网下载所需的版本，之后安装并设置环境变量即可。以上步骤完成之后同样在命令行中测试<code>groovy -version</code>，看看能够正确显示Groovy的版本号。</p><h2 id="Groovy自带编辑器"><a href="#Groovy自带编辑器" class="headerlink" title="Groovy自带编辑器"></a>Groovy自带编辑器</h2><p>使用终端或者命令行执行<code>groovyConsole</code>等待一下即可打开Groovy自带的编辑器，我们可以直接在上面写程序，之后使用快捷键<code>Ctrl+R</code>可以执行代码查看执行结果；使用快捷键<code>Ctrl+W</code>可以清空控制台的输出信息。另外需要了解的一点就是Groovy的代码需要保存在.groovy文件中。</p><h1 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h1><p>Groovy与JAVA有很多的相同点，在注释上尤为明显，举例如下：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//单行注释</span></span><br><span class="line">println <span class="string">"single line comments"</span> <span class="comment">//注释可以位于语句后方</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 多行</span></span><br><span class="line"><span class="comment">   注释 */</span></span><br><span class="line">println <span class="string">"multiline comments"</span> <span class="comment">/*多行注释还可以</span></span><br><span class="line"><span class="comment">这样写*/</span></span><br><span class="line">println <span class="number">1</span> <span class="comment">/*还可以写在语句中间*/</span> +<span class="number">2</span> <span class="comment">/*没错，这样写也是可以的*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//文档注释与JAVA类似</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 类说明</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> &#123;</span></span><br><span class="line">    <span class="comment">/** 变量说明 */</span></span><br><span class="line">    String name</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 说明方法的含义</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> 参数含义</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 返回值含义</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    String greet(String otherPerson) &#123;</span><br><span class="line">       <span class="string">"Hello $&#123;otherPerson&#125;"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//特殊的单行注释，这种注释通常是用来给UNIX系统声明允许脚本运行的类型的</span></span><br><span class="line"><span class="meta">#!/usr/bin/env groovy</span></span><br><span class="line">println <span class="string">"Hello from the shebang line"</span></span><br></pre></td></tr></table></figure></p><h1 id="关键字"><a href="#关键字" class="headerlink" title="关键字"></a>关键字</h1><table><thead><tr><th style="text-align:left"></th><th style="text-align:left"></th><th style="text-align:left"></th><th style="text-align:left"></th></tr></thead><tbody><tr><td style="text-align:left">as</td><td style="text-align:left">assert</td><td style="text-align:left">break</td><td style="text-align:left">case</td></tr><tr><td style="text-align:left">catch</td><td style="text-align:left">class</td><td style="text-align:left">const</td><td style="text-align:left">continue</td></tr><tr><td style="text-align:left">def</td><td style="text-align:left">default</td><td style="text-align:left">do</td><td style="text-align:left">else</td></tr><tr><td style="text-align:left">enum</td><td style="text-align:left">extends</td><td style="text-align:left">false</td><td style="text-align:left">finally</td></tr><tr><td style="text-align:left">for</td><td style="text-align:left">goto</td><td style="text-align:left">if</td><td style="text-align:left">implements</td></tr><tr><td style="text-align:left">import</td><td style="text-align:left">in</td><td style="text-align:left">instanceof</td><td style="text-align:left">interface</td></tr><tr><td style="text-align:left">new</td><td style="text-align:left">null</td><td style="text-align:left">package</td><td style="text-align:left">return</td></tr><tr><td style="text-align:left">super</td><td style="text-align:left">switch</td><td style="text-align:left">this</td><td style="text-align:left">throw</td></tr><tr><td style="text-align:left">throws</td><td style="text-align:left">trait</td><td style="text-align:left">true</td><td style="text-align:left">try</td></tr><tr><td style="text-align:left">while</td><td style="text-align:left"></td><td style="text-align:left"></td></tr></tbody></table><h1 id="标识符"><a href="#标识符" class="headerlink" title="标识符"></a>标识符</h1><h2 id="普通标识符"><a href="#普通标识符" class="headerlink" title="普通标识符"></a>普通标识符</h2><p>标识符以字母，美元符号或下划线开始。不能以一个数字开始。<br>合法标识符如下：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> name</span><br><span class="line"><span class="keyword">def</span> item3</span><br><span class="line"><span class="keyword">def</span> with_underscore</span><br><span class="line"><span class="keyword">def</span> $dollarStart</span><br></pre></td></tr></table></figure></p><p>下面是一些非法标识符：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="number">3</span>tier</span><br><span class="line"><span class="keyword">def</span> a+b</span><br><span class="line">def a#b</span><br></pre></td></tr></table></figure></p><p>特殊一点的就是关键字在.之后的都可以作为合法的标识符：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">foo.<span class="keyword">as</span></span><br><span class="line">foo.<span class="keyword">assert</span></span><br><span class="line">foo.<span class="keyword">break</span></span><br><span class="line">foo.<span class="keyword">case</span></span><br><span class="line">foo.<span class="keyword">catch</span></span><br></pre></td></tr></table></figure></p><h2 id="引用标识符"><a href="#引用标识符" class="headerlink" title="引用标识符"></a>引用标识符</h2><p>引用标识符是指出现在<code>.</code>表达式的后面，例如person.”name”中的name，一般的用法如下：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> map = [:]</span><br><span class="line"></span><br><span class="line">map.<span class="string">"an identifier with a space and double quotes"</span> = <span class="string">"ALLOWED"</span><span class="comment">// 向map添加一个键值对</span></span><br><span class="line">map.<span class="string">'with-dash-signs-and-single-quotes'</span> = <span class="string">"ALLOWED"</span><span class="comment">// 向map再添加一个键值对</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> map.<span class="string">"an identifier with a space and double quotes"</span> == <span class="string">"ALLOWED"</span></span><br><span class="line"><span class="keyword">assert</span> map.<span class="string">'with-dash-signs-and-single-quotes'</span> == <span class="string">"ALLOWED"</span></span><br></pre></td></tr></table></figure></p><p>另外由于Groovy支持多种字符串表达方式(接下来会讲到)，所以以下的表达方式也都是允许的：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">map.<span class="string">'single quote'</span></span><br><span class="line">map.<span class="string">"double quote"</span></span><br><span class="line">map.<span class="string">'''triple single quote'''</span></span><br><span class="line">map.<span class="string">"""triple double quote"""</span></span><br><span class="line">map.<span class="regexp">/slashy string/</span></span><br><span class="line">map.<span class="string">$/dollar slashy string/$</span></span><br></pre></td></tr></table></figure></p><p>此外，由于Groovy提供了自己的String，称之为GString，他是一种有插值的字符串(即可以在字符串中引用其它变量)，所以以下的这些也算是合法的标识符：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> firstname = <span class="string">"Homer"</span></span><br><span class="line">map.<span class="string">"Simpson-$&#123;firstname&#125;"</span> = <span class="string">"Homer Simpson"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> map.<span class="string">'Simpson-Homer'</span> == <span class="string">"Homer Simpson"</span></span><br></pre></td></tr></table></figure></p><h1 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h1><p>Groovy中的字符串有两大类，第一种与JAVA的一样，即java.lang.String，另一种则是Groovy独有的：groovy.lang.GString，后者的作用就是可以在字符串中引用其他变量(即插值)。</p><h2 id="单引号字符串"><a href="#单引号字符串" class="headerlink" title="单引号字符串"></a>单引号字符串</h2><p>单引号字符串是普通的java.lang.String，不支持插值。<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'a single quoted string'</span></span><br></pre></td></tr></table></figure></p><h2 id="三单引号字符串"><a href="#三单引号字符串" class="headerlink" title="三单引号字符串"></a>三单引号字符串</h2><p>三单引号字符串同样也是普通的java.lang.String，也是不支持插值。<br>三单引号字符串是可以跨行和保留所有的缩进：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> startingAndEndingWithANewline = <span class="string">'''</span></span><br><span class="line"><span class="string">line one</span></span><br><span class="line"><span class="string">line two</span></span><br><span class="line"><span class="string">line three</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">assert</span> startingAndEndingWithANewline.startsWith(<span class="string">'\n'</span>)</span><br><span class="line"><span class="keyword">assert</span> startingAndEndingWithANewline.endsWith(<span class="string">'\n'</span>)</span><br></pre></td></tr></table></figure></p><p>上述的字符串中第一行与最后一行都是换行，可见三单引号字符串是保留了所有的换行，而实际上，Groovy将其中的换行解释为<code>\n</code>，将所有的空白字符则是保留原样。<br>如果我们想要转义字符的话只要在需要转义的字符前方添加反斜杠<code>\</code>，例如我们想要去掉上方字符串的前后两个换行，则可以这样处理<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> startingAndEndingWithoutANewline = <span class="string">'''\</span></span><br><span class="line"><span class="string">line one</span></span><br><span class="line"><span class="string">line two</span></span><br><span class="line"><span class="string">line three\</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">assert</span> !startingAndEndingWithoutANewline.startsWith(<span class="string">'\n'</span>)</span><br><span class="line"><span class="keyword">assert</span> !startingAndEndingWithoutANewline.endsWith(<span class="string">'\n'</span>)</span><br></pre></td></tr></table></figure></p><h2 id="双引号字符串"><a href="#双引号字符串" class="headerlink" title="双引号字符串"></a>双引号字符串</h2><p>双引号字符串稍微有点特殊，刚才我们说的插值就是在这里使用的，请看下方的例子：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> name = <span class="string">"Guillaume"</span> <span class="comment">// 普通字符串</span></span><br><span class="line"><span class="keyword">def</span> greeting = <span class="string">"Hello $&#123;name&#125;"</span> <span class="comment">// 含有插值的字符串</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> greeting.toString() == <span class="string">'Hello Guillaume'</span></span><br></pre></td></tr></table></figure></p><p>可以看到没有含有插值的字符串就是普通的JAVA字符串，含有的则是GString，他可以访问其他的变量，表达的方式有两种：<code>${}</code>和<code>$</code>，前者一般用于代替字符串或者表达式，后者则用于A.B的形式中，且也只能适用于这种形式中，如果表达式中含有例如括号，大括号，闭包(稍后讲解)等都是会报错的：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> sum = <span class="string">"The sum of 2 and 3 equals $&#123;2 + 3&#125;"</span></span><br><span class="line"><span class="keyword">assert</span> sum.toString() == <span class="string">'The sum of 2 and 3 equals 5'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> person = [<span class="string">name:</span> <span class="string">'Guillaume'</span>, <span class="string">age:</span> <span class="number">36</span>]</span><br><span class="line"><span class="keyword">assert</span> <span class="string">"$person.name is $person.age years old"</span> == <span class="string">'Guillaume is 36 years old'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> number = <span class="number">3.14</span></span><br><span class="line">shouldFail(MissingPropertyException) &#123;</span><br><span class="line">    <span class="comment">// 这样写是会报错的，因为解释器会将其解释为："$&#123;number.toString&#125;()"</span></span><br><span class="line">    println <span class="string">"$number.toString()"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果想要转义$或者${}占位符，只需要加上反斜杠<code>\</code>就行<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span> <span class="string">'$&#123;name&#125;'</span> == <span class="string">"\$&#123;name&#125;"</span></span><br></pre></td></tr></table></figure></p><p>java.lang.String与groovy.lang.GString配合使用也是我们经常遇到的，在下面的例子中，takeString方法需要的参数是String类型的，但是我们传进去的却是GString类型的，此时GSTring的toString()方法会被调用。使得传入时能够保证类型一致。</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">String takeString(String message) &#123; </span><br><span class="line">  <span class="keyword">assert</span> message <span class="keyword">instanceof</span> String </span><br><span class="line">  <span class="keyword">return</span> message</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> message = <span class="string">"The message is $&#123;'hello'&#125;"</span> </span><br><span class="line"><span class="keyword">assert</span> message <span class="keyword">instanceof</span> GString </span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> result = takeString(message) </span><br><span class="line"><span class="keyword">assert</span> result <span class="keyword">instanceof</span> String</span><br><span class="line"><span class="keyword">assert</span> result == <span class="string">'The message is hello'</span></span><br></pre></td></tr></table></figure><p>最后需要注意一点的就是，普通Java字符串是不可变的，而一个GString依赖于插入的值，它的String是可变的。即使有相同的字符串结果，GString和String也没有相同的hashCode，例如：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span> <span class="string">"one: $&#123;1&#125;"</span>.hashCode() != <span class="string">"one: 1"</span>.hashCode()</span><br></pre></td></tr></table></figure></p><p>所以在选择Map的键值时我们应当避免选择GString，看看下面的例子你就清楚了：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> key = <span class="string">"a"</span></span><br><span class="line"><span class="keyword">def</span> m = [<span class="string">"$&#123;key&#125;"</span>: <span class="string">"letter $&#123;key&#125;"</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> m[<span class="string">"a"</span>] == <span class="literal">null</span></span><br></pre></td></tr></table></figure></p><h2 id="其他字符串"><a href="#其他字符串" class="headerlink" title="其他字符串"></a>其他字符串</h2><p>Groovy中还有另外三种字符串，分别是：三双引号字符串、斜杠字符串、美元符修饰的斜杠字符串，由于这三种字符串用得比较少，这里就略过了，有兴趣的朋友可以直接到<a href="http://www.groovy-lang.org/syntax.html" target="_blank" rel="noopener">Groovy的官方文档</a>中查阅。</p><h1 id="列表List"><a href="#列表List" class="headerlink" title="列表List"></a>列表List</h1><p>由于Groovy中没有定义任何集合类，因此List也是沿用了java.util.List，其默认的实现则是java.util.ArrayList，不过我们也可以使用as关键字将其转换。<br>变量定义：List 变量由<code>[ ]</code>定义，比如：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> aList = [<span class="number">5</span>,<span class="string">'string'</span>,<span class="literal">true</span>] <span class="comment">//List 由[]定义，其元素可以是任何对象</span></span><br></pre></td></tr></table></figure></p><p>变量存取：可以直接通过索引存取，而且不用担心索引越界。如果索引超过当前链表长度，List会自动往该索引添加元素<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span> aList[<span class="number">1</span>] == <span class="string">'string'</span> </span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">-1</span>] == <span class="literal">true</span>  <span class="comment">//当索引为-1时表示倒数最后一个</span></span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">-2</span>] == <span class="string">'string'</span>  <span class="comment">//当索引为-2时表示倒数第二个，以此类推</span></span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">5</span>] == <span class="literal">null</span> <span class="comment">//第 6 个元素为空  </span></span><br><span class="line">aList[<span class="number">100</span>] = <span class="number">100</span>  <span class="comment">//设置第 101 个元素的值为 100</span></span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">100</span>] == <span class="number">100</span></span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">0</span>,<span class="number">2</span>] == [<span class="number">5</span>,<span class="literal">true</span>] <span class="comment">// 一次访问两个元素，并返回一个包含这两个元素的新列表</span></span><br><span class="line"><span class="keyword">assert</span> aList[<span class="number">0.</span><span class="number">.2</span>] == [<span class="number">6</span>,<span class="string">'string'</span>,<span class="literal">true</span>] <span class="comment">// 使用范围访问列表中这个范围内的元素，从start到end元素位置</span></span><br></pre></td></tr></table></figure></p><p>那么，aList 到现在为止有多少个元素呢？<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">println aList.size()  <span class="comment">// 结果是 101</span></span><br></pre></td></tr></table></figure></p><p>现在我们打算将ArrayList转换为其他的具体实现类<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">def arrayList = [1, 2, 3]</span><br><span class="line">assert arrayList instanceof java.util.ArrayList // 默认情况下是ArrayLis</span><br><span class="line"></span><br><span class="line">def linkedList = [2, 3, 4] as LinkedList // 使用as关键字将其转换为LinkedList</span><br><span class="line">assert linkedList instanceof java.util.LinkedList </span><br><span class="line"></span><br><span class="line">LinkedList otherLinked = [3, 4, 5] // 声明的时候直接指定为LinkedList</span><br><span class="line">assert otherLinked instanceof java.util.LinkedList</span><br></pre></td></tr></table></figure></p><h1 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h1><p>数组的声明与List类似，且都是用<code>[ ]</code>来表示的，之所以不能像JAVA一样采用{}来初始化数组，是因为在Groovy中{}会被误解为闭包标志。我们需要通过类型转换或者类型定义来定义数组，与List不同的是，数组中的元素类型需要一致，并且数组的大小不能改变：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">String[] arrStr = [<span class="string">'Ananas'</span>, <span class="string">'Banana'</span>, <span class="string">'Kiwi'</span>] <span class="comment">//可以在定义的时候直接指定为数组，</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> arrStr <span class="keyword">instanceof</span> String[] </span><br><span class="line"><span class="keyword">assert</span> !(arrStr <span class="keyword">instanceof</span> List)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> numArr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] <span class="keyword">as</span> <span class="keyword">int</span>[] <span class="comment">// 使用as关键字转换成数组</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> numArr <span class="keyword">instanceof</span> <span class="keyword">int</span>[] </span><br><span class="line">numArr[<span class="number">0</span>] = <span class="number">3</span></span><br><span class="line"><span class="keyword">assert</span> numArr[<span class="number">0</span>] == <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> numArr.size() == <span class="number">3</span></span><br></pre></td></tr></table></figure></p><h1 id="映射Map"><a href="#映射Map" class="headerlink" title="映射Map"></a>映射Map</h1><p>变量定义：Map 变量由<code>[:]</code>定义，比如<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> aMap = [<span class="string">'key1'</span>:<span class="string">'value1'</span>,<span class="string">'key2'</span>:<span class="literal">true</span>]</span><br></pre></td></tr></table></figure></p><p>Map 由 [:] 定义，注意其中的冒号。冒号左边是 key，右边是value。在Groovy中key 不一定是字符串，也可以是其他对象，value也可以是任何对象。另外，key 可以用’’或”” 包起来，也可以不用引号包起来。比如：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> aNewMap = [<span class="string">key1:</span><span class="string">"value"</span>,<span class="string">key2:</span><span class="literal">true</span>] <span class="comment">//其中的 key1 和 key2 默认被处理成字符串 "key1" 和 "key2" </span></span><br><span class="line"><span class="keyword">assert</span> aNewMap.<span class="string">"key1"</span> == <span class="string">"value"</span></span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">不过 key 要是不使用引号包起来的话，也会带来一定混淆，比如：</span><br><span class="line"></span><br><span class="line">```groovy</span><br><span class="line"><span class="keyword">def</span> key1=<span class="string">"wowo"</span></span><br><span class="line"><span class="keyword">def</span> aConfusedMap=[<span class="string">key1:</span><span class="string">"who am i?"</span>]</span><br><span class="line"><span class="comment">//aConfuseMap 中的 key1 到底是"key1"还是变量 key1 的值“wowo”？显然，答案是字符串"key1"。如果要是"wowo"的话，则 aConfusedMap 的定义必须设置成：  </span></span><br><span class="line"><span class="keyword">def</span> aConfusedMap=[(key1):<span class="string">"who am i?"</span>]</span><br></pre></td></tr></table></figure></p><p>Map 中元素的存取更加方便，它支持多种方法：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> aMap = [<span class="string">key1:</span><span class="number">1</span>,<span class="string">key2:</span><span class="number">2</span>,<span class="string">key3:</span><span class="number">3</span>,<span class="number">4</span>:<span class="number">4</span>]</span><br><span class="line"><span class="keyword">assert</span> aMap.key1 == <span class="number">1</span>  <span class="comment">//这种表达方法好像 key1 就是 aMap 的一个成员变量一样  </span></span><br><span class="line"><span class="keyword">assert</span> aMap.<span class="string">"key1"</span> ==<span class="number">1</span>   <span class="comment">//因为我们将key1看成了字符串，因此也可以直接这样访问</span></span><br><span class="line"><span class="keyword">assert</span> aMap[<span class="string">'key1'</span>] ==<span class="number">1</span> <span class="comment">//这种表达方法更传统一点  </span></span><br><span class="line"><span class="keyword">assert</span> aMap[<span class="number">4</span>] ==<span class="number">4</span> <span class="comment">//对于数字则需要使用这种方式来访问了</span></span><br><span class="line">aMap.anotherkey = <span class="string">"i am map"</span>  <span class="comment">//为 map 添加新元素 </span></span><br><span class="line"><span class="keyword">assert</span> aMap == [<span class="string">key1:</span><span class="number">1</span>, <span class="string">key2:</span><span class="number">2</span>, <span class="string">key3:</span><span class="number">3</span>, <span class="number">4</span>:<span class="number">4</span>, <span class="string">anotherkey:</span><span class="string">"i am map"</span>]</span><br></pre></td></tr></table></figure></p><h1 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h1><h2 id="闭包的定义"><a href="#闭包的定义" class="headerlink" title="闭包的定义"></a>闭包的定义</h2><p>闭包(Closure)是Groovy中一种非常重要的数据类型，他是一段可以执行的代码。定义的格式如下：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> xxx = &#123;paramters -&gt; code&#125;  <span class="comment">//参数可以是0个或者多个，每个参数之间用`,`分隔开；后面的代码可以一行也可以多行</span></span><br><span class="line"><span class="keyword">def</span> xxx = &#123;无参数只有 code&#125;  <span class="comment">//这种没有参数的情况则不需要-&gt;符号</span></span><br></pre></td></tr></table></figure></p><p>根据上面的格式我们再来看看几个具体的实例：</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123; item++ &#125;                    <span class="comment">//1                      </span></span><br><span class="line">&#123; println it &#125;                <span class="comment">//2                     </span></span><br><span class="line">&#123; it -&gt; println it &#125;          <span class="comment">//3                      </span></span><br><span class="line">&#123; name -&gt; println name &#125;      <span class="comment">//4                       </span></span><br><span class="line">&#123; -&gt; item++ &#125;                 <span class="comment">//5                       </span></span><br><span class="line">&#123; String x, <span class="keyword">int</span> y -&gt;                                </span><br><span class="line">    println <span class="string">"hey $&#123;x&#125; the value is $&#123;y&#125;"</span>   <span class="comment">//6</span></span><br><span class="line">&#125;</span><br><span class="line">&#123; String x, <span class="keyword">int</span> y=<span class="number">6</span> -&gt;                                </span><br><span class="line">    println <span class="string">"hey $&#123;x&#125; the value is $&#123;y&#125;"</span>   <span class="comment">//7</span></span><br><span class="line">&#125;</span><br><span class="line">&#123; reader -&gt;                                         </span><br><span class="line">    <span class="keyword">def</span> line = reader.readLine()            <span class="comment">//8</span></span><br><span class="line">    line.trim()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的几个例子中：</p><ul><li>1、闭包中引用了一个item变量，并做自增操作；</li><li>2、闭包中直接打印it变量，it变量是闭包中默认的参数，他就是第三行代码的缩减形式；</li><li>3、与上一行其实是一样的，这里只是显示的将it变量标了出来；</li><li>4、与第三行代码其实是一个道理只是将参数名字改了一下而已；</li><li>5、这里没有参数只有一个箭头符号与代码，这样写的意思就是闭包中没有任何参数，连默认的参数都没有，所以如果我们调用闭包的时候传入了参数，那么就会报错；</li><li>6、这个闭包定义了两个指定类型的参数，并在代码中使用插值引用了两个参数的值；</li><li>7、这个闭包相比上个闭包只是在参数定义上有了区别，在参数定义的时候可以指定默认值；</li><li>8、这个闭包定义了一个没有指定类型的参数，并且代码块中是可以有多个表达式的。</li></ul><p>除此之外闭包也是groovy.lang.Closure类的实例，尽管它是一个代码块，但是它可以赋给一个变量或者一个属性作为其他的变量，请看如下的实例：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> listener = &#123; e -&gt; println <span class="string">"Clicked on $e.source"</span> &#125;   <span class="comment">//1   </span></span><br><span class="line"><span class="keyword">assert</span> listener <span class="keyword">instanceof</span> Closure                       </span><br><span class="line">Closure callback = &#123; println <span class="string">'Done!'</span> &#125;                   <span class="comment">//2                      </span></span><br><span class="line">Closure&lt;Boolean&gt; isTextFile = &#123;                          <span class="comment">//3</span></span><br><span class="line">    File it -&gt; it.name.endsWith(<span class="string">'.txt'</span>)               <span class="comment">//4      </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面的几个例子中：</p><ul><li>1、闭包是可以赋值给一个变量的；</li><li>2、如果不使用def关键字来声明变量，我们可以把闭包赋值给一个groovy.lang.Closure类型的变量；</li><li>3、我们可以指定闭包的返回类型，这里的返回类型为Boolean，当然这个是可选的。</li><li>4、闭包中代码的最后一行就是闭包的返回值，也可以显示的使用return关键字，也可以不写。</li></ul><h2 id="闭包的调用"><a href="#闭包的调用" class="headerlink" title="闭包的调用"></a>闭包的调用</h2><p>调用闭包有两种方式：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> isOdd = &#123; <span class="keyword">int</span> i-&gt; i%<span class="number">2</span> == <span class="number">1</span> &#125;  <span class="comment">//1                      </span></span><br><span class="line"><span class="keyword">assert</span> isOdd(<span class="number">3</span>) == <span class="literal">true</span>           <span class="comment">//2                             </span></span><br><span class="line"><span class="keyword">assert</span> isOdd.call(<span class="number">2</span>) == <span class="literal">false</span>     <span class="comment">//3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> isEven = &#123; it%<span class="number">2</span> == <span class="number">0</span> &#125;        <span class="comment">//4                               </span></span><br><span class="line"><span class="keyword">assert</span> isEven(<span class="number">3</span>) == <span class="literal">false</span>         <span class="comment">//5                               </span></span><br><span class="line"><span class="keyword">assert</span> isEven.call(<span class="number">2</span>) == <span class="literal">true</span>     <span class="comment">//6</span></span><br></pre></td></tr></table></figure></p><p>总结来说就是<code>闭包对象.call(参数)</code>或者直接<code>闭包对象.(参数)</code>，第4行中是省略了闭包参数的写法，直接使用it来代替，所以我们调用的时候需要传入一个参数。<br>当闭包作为闭包或方法的最后一个参数，可以将闭包从参数圆括号中提取出来只接在最后；如果闭包或者方法有一个参数且该参数为闭包，那么可以直接省略掉闭包或方法的圆括号；又如果闭包或者方法的参数中有多个闭包，并且这些闭包必须都要排在参数列表中的最后，则此时这些闭包可以依次写在圆括号外：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> testClosure=&#123;param,c -&gt; c(c(param))&#125;            <span class="comment">//1</span></span><br><span class="line"><span class="keyword">def</span> testMethod(a1, b1, closure)&#123;                    <span class="comment">//2</span></span><br><span class="line">      closure() <span class="comment">//调用闭包  </span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> testClosure(<span class="number">5</span>)&#123;it*<span class="number">3</span>&#125; == <span class="number">45</span>                   <span class="comment">//3</span></span><br><span class="line">testMethod(<span class="number">1</span>,<span class="string">'test'</span>)&#123;println(<span class="string">"i am in closure"</span>)&#125;    <span class="comment">//4</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> testClosure2=&#123;c -&gt; c()&#125;                         <span class="comment">//5</span></span><br><span class="line"><span class="keyword">def</span> testMethod2(a1, b1, closure, closure2)&#123;         <span class="comment">//6</span></span><br><span class="line">      closure() <span class="comment">// 调用闭包1</span></span><br><span class="line">      closure2() <span class="comment">// 调用闭包2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> testClosure2&#123;<span class="string">"i am in testClosure2"</span>&#125; == <span class="string">"i am in testClosure2"</span>         <span class="comment">//7</span></span><br><span class="line">testMethod2(<span class="number">1</span>,<span class="string">'test'</span>)</span><br><span class="line">    &#123;println(<span class="string">"i am in closure"</span>)&#125; </span><br><span class="line">    &#123;println <span class="string">"i am in closure2"</span>&#125;                    <span class="comment">//8</span></span><br></pre></td></tr></table></figure></p><p>在上面的例子中：</p><ul><li>1、闭包c是作为闭包testClosure的最后一个参数；</li><li>2、闭包closure作为方法testMethod的最后一个参数；</li><li>3、在调用闭包testClosure时，可以将闭包c写在圆括号外，圆括号内只需要填写其他参数即可；</li><li>4、同样的道理，闭包写在圆括号外面。</li><li>5、当定义方法或者闭包时只有一个参数，且参数为闭包；</li><li>6、当定义的方法或者闭包的参数列表中有多个闭包，并且这些闭包需要排列在参数列表的末尾；</li><li>7、调用testClosure2闭包时可以直接省略闭包的圆括号，这种方式也适用于方法；</li><li>8、调用testMethod2方法时可以直接将闭包写在圆括号外面，多个闭包以此排列并且不需要使用<code>,</code>将每个闭包分隔开，这种方式也适用于闭包。</li></ul><h2 id="闭包的可变参数"><a href="#闭包的可变参数" class="headerlink" title="闭包的可变参数"></a>闭包的可变参数</h2><p>闭包可以像其它方法那样声明一个可变的参数列表。当参数列表的最后一个参数的长度是可变的时候（或者是一个数组），那么这个闭包就能接收数量不定的参数，请看下面的实例：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> concat1 = &#123; String... args -&gt; args.join(<span class="string">''</span>) &#125;        <span class="comment">//1   </span></span><br><span class="line"><span class="keyword">assert</span> concat1(<span class="string">'abc'</span>,<span class="string">'def'</span>) == <span class="string">'abcdef'</span>                  <span class="comment">//2  </span></span><br><span class="line"><span class="keyword">def</span> concat2 = &#123; String[] args -&gt; args.join(<span class="string">''</span>) &#125;         <span class="comment">//3   </span></span><br><span class="line"><span class="keyword">assert</span> concat2(<span class="string">'abc'</span>, <span class="string">'def'</span>) == <span class="string">'abcdef'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> multiConcat = &#123; <span class="keyword">int</span> n, String... args -&gt;             <span class="comment">//4   </span></span><br><span class="line">    args.join(<span class="string">''</span>)*n</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">assert</span> multiConcat(<span class="number">2</span>, <span class="string">'abc'</span>,<span class="string">'def'</span>) == <span class="string">'abcdefabcdef'</span></span><br></pre></td></tr></table></figure></p><p>上面的例子中：</p><ul><li>1、闭包可以接收数量可变的String参数；</li><li>2、可以在调用时使用任意数量的参数，而且不用被包装成一个数组；</li><li>3、使用数组也能达到同样的效果；</li><li>4、只要最后一个参数是一个数组或者可变长度的的参数类型即可。</li></ul><h2 id="在GString中使用闭包"><a href="#在GString中使用闭包" class="headerlink" title="在GString中使用闭包"></a>在GString中使用闭包</h2><p>之前我们讲到GString与传统的java.lang.String的区别，而在GString中使用闭包一般有两种情形：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> x = <span class="number">1</span></span><br><span class="line"><span class="keyword">def</span> gs1 = <span class="string">"x = $&#123;x&#125;"</span>          <span class="comment">//1</span></span><br><span class="line"><span class="keyword">assert</span> gs1 == <span class="string">'x = 1'</span>         <span class="comment">//2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> gs2 = <span class="string">"x = $&#123;-&gt; x&#125;"</span>       <span class="comment">//3</span></span><br><span class="line"><span class="keyword">assert</span> gs2 == <span class="string">'x = 1'</span>         <span class="comment">//4</span></span><br><span class="line"></span><br><span class="line">x = <span class="number">2</span>         <span class="comment">//5</span></span><br><span class="line"><span class="keyword">assert</span> gs1 == <span class="string">'x = 1'</span>         <span class="comment">//6</span></span><br><span class="line"><span class="keyword">assert</span> gs2 == <span class="string">'x = 2'</span>         <span class="comment">//7</span></span><br></pre></td></tr></table></figure></p><p>上面例子中的第一行以及第三行就是在GString中使用闭包的两种情形，严格意义上来讲，第一行代码中的<code>${}</code>并不是闭包，而只是一个插值的符号。<br>在注释1与注释3中定义了两个GString之后我们在注释2以及注释4中验证了他们的结果，这个结果也确实与我们预料中的一致。<br>但是接下来的注释5中，我们修改了变量x的值，将其改为2，现在再来看看两个GString的结果，发现采用<code>${}</code>的结果不变，而使用了闭包<code>${-&gt;}</code>的则会更新结果，这究竟是为什么呢？<br>这个主要是因为Groovy对两种符号的加载方式不一样，前者<code>${}</code>名字叫做<code>eagerGString</code>，它的值在GString被创建时就已经确定了，并且一直指向旧的这个对象，当我们去改变他所引用的x对象时，它是不会更新的；而在后者<code>${-&gt;}</code>中，Groovy采用了懒加载(lazy evaluation)的方式，即每次GString转换为String的时候都会再次调用闭包，因此它就可以更新。<br>下面我们再来看一下两个实例加深一下对两者的理解：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> &#123;</span></span><br><span class="line">    String name</span><br><span class="line">    String toString() &#123; name &#125;              </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">def</span> sam = <span class="keyword">new</span> Person(<span class="string">name:</span><span class="string">'Sam'</span>)           </span><br><span class="line"><span class="keyword">def</span> lucy = <span class="keyword">new</span> Person(<span class="string">name:</span><span class="string">'Lucy'</span>)      </span><br><span class="line"><span class="keyword">def</span> p = sam                <span class="comment">//1                 </span></span><br><span class="line"><span class="keyword">def</span> gs = <span class="string">"Name: $&#123;p&#125;"</span>                <span class="comment">//2           </span></span><br><span class="line"><span class="keyword">assert</span> gs == <span class="string">'Name: Sam'</span>             <span class="comment">//3           </span></span><br><span class="line">p = lucy                <span class="comment">//4    </span></span><br><span class="line"><span class="keyword">assert</span> gs == <span class="string">'Name: Sam'</span>             <span class="comment">//5        </span></span><br><span class="line">sam.name = <span class="string">'Lucy'</span>                    <span class="comment">//6    </span></span><br><span class="line"><span class="keyword">assert</span> gs == <span class="string">'Name: Lucy'</span>            <span class="comment">//7</span></span><br></pre></td></tr></table></figure></p><p>上面的代码中：</p><ul><li>1、首先将对象sam赋值给变量p；</li><li>2、使用了插值符号来调用对象的toString()方法；</li><li>3、很显然返回的名字是Sam；</li><li>4、如果将lucy赋值给p变量；</li><li>5、其依旧指向p变量之前指向的旧对象而不会因为p所指的对象的改变而改变GString的值；</li><li>6、但是如果我们只是改变了旧对象的属性；</li><li>7、那么很显然GString的值还是会更新的。</li><li>因此如果使用的是<code>${}</code>的形式，那么改变所指的对象是不能更新GString的值，但是改变对象的属性的话是可以的。</li></ul><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> &#123;</span></span><br><span class="line">    String name</span><br><span class="line">    String toString() &#123; name &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">def</span> sam = <span class="keyword">new</span> Person(<span class="string">name:</span><span class="string">'Sam'</span>)</span><br><span class="line"><span class="keyword">def</span> lucy = <span class="keyword">new</span> Person(<span class="string">name:</span><span class="string">'Lucy'</span>)</span><br><span class="line"><span class="keyword">def</span> p = sam</span><br><span class="line"><span class="comment">// Create a GString with lazy evaluation of "p"</span></span><br><span class="line"><span class="keyword">def</span> gs = <span class="string">"Name: $&#123;-&gt; p&#125;"</span></span><br><span class="line"><span class="keyword">assert</span> gs == <span class="string">'Name: Sam'</span></span><br><span class="line">p = lucy</span><br><span class="line"><span class="keyword">assert</span> gs == <span class="string">'Name: Lucy'</span></span><br></pre></td></tr></table></figure><p>同样的例子，如果是选择了${-&gt;}的形式，那么改变所指的对象是可以更新GString的值的，这就是两种形式的区别。</p><h1 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h1><h2 id="方法的声明"><a href="#方法的声明" class="headerlink" title="方法的声明"></a>方法的声明</h2><p>Groovy中的方法与JAVA的差别不大，声明方法时可以加上一个返回值的类型，或者直接使用def关键字而无需标注返回值类型，采用这种方法的时候方法的返回值类型的不固定的。方法可以接收任意数量的参数，并且参数的类型可以是无类型的也可以是有类型的还可以是有默认值的。我们在java中使用的一些修饰方法的关键词都是可以用来修饰Groovy中的方法，但是在Groovy中默认情况下不适用任何访问修饰词的情况下是代表public的。<br>最后需要再说一下的是，Groovy中的方法总是会返回值的，如果方法中没有return语句，那么方法中的最后一行将会是该方法的返回值，具体的可以看下面的例子。<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> someMethod() &#123; <span class="string">'method called'</span> &#125; <span class="comment">//没有参数也没有return关键字并且使用def来声明方法                     </span></span><br><span class="line">String anotherMethod() &#123; <span class="string">'another method called'</span> &#125; <span class="comment">//方法的返回值确定         </span></span><br><span class="line"><span class="keyword">def</span> thirdMethod(param1) &#123; <span class="string">"$param1 passed"</span> &#125; <span class="comment">//方法接收参数，并且参数的类型是未确定的         </span></span><br><span class="line"><span class="keyword">static</span> String fourthMethod(String param1) &#123; <span class="string">"$param1 passed"</span> &#125; <span class="comment">//静态方法，并且参数是有类型的</span></span><br><span class="line"><span class="comment">//方法接收三个参数，第一个是无类型的，第二个是String类型，第三个是int类型并且默认值是6</span></span><br><span class="line">String fifthMethod(param1,String param2,<span class="keyword">int</span> param3 = <span class="number">6</span>) &#123;<span class="string">"$param1,$param2,$param3"</span>&#125;   </span><br><span class="line"><span class="keyword">assert</span> fifthMethod(<span class="number">1</span>,<span class="string">"111"</span>) == <span class="string">"1,111,6"</span> <span class="comment">//如果调用的时候不传入默认的那个参数，则返回结果就会继续使用默认值</span></span><br><span class="line"><span class="keyword">assert</span> fifthMethod(<span class="number">1</span>,<span class="string">"111"</span>,<span class="number">5</span>) == <span class="string">"1,111,5"</span> <span class="comment">//如果传入了第三个参数，那么默认值将会被覆盖</span></span><br></pre></td></tr></table></figure></p><h2 id="可变参数的方法"><a href="#可变参数的方法" class="headerlink" title="可变参数的方法"></a>可变参数的方法</h2><p>在讲闭包的时候我们已经讲到了可变参数，方法中的可变参数与闭包的一致，这里就不再赘述，下面讲一下两种特殊情况：</p><ul><li>当方法接收可变参数时，我们调用方法时传入的参数为null</li></ul><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> foo(Object... args) &#123; args &#125;      <span class="comment">//1</span></span><br><span class="line"><span class="keyword">assert</span> foo(<span class="literal">null</span>) == <span class="literal">null</span>              <span class="comment">//2</span></span><br><span class="line">asser foo(<span class="literal">null</span>,<span class="literal">null</span>) == [<span class="literal">null</span>,<span class="literal">null</span>]   <span class="comment">//3</span></span><br></pre></td></tr></table></figure><p>我们声明的方法foo，他接收一个可变参数，在第二行中我们传入一个null，此时返回的是空，这里的空并不是一个包含<code>null</code>元素的长度为1的数组；但是在第三行中当我们传入了两个null时，却发现返回的确实长度为2的包含两个null元素的数组。这种情况是需要我们在实践中注意的。</p><ul><li>当可变函数方法遇到重载方法时</li></ul><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> foo(Object... args) &#123; <span class="number">1</span> &#125;</span><br><span class="line"><span class="keyword">def</span> foo(Object x) &#123; <span class="number">2</span> &#125;</span><br><span class="line"><span class="keyword">assert</span> foo() == <span class="number">1</span>               <span class="comment">//1</span></span><br><span class="line"><span class="keyword">assert</span> foo(<span class="number">1</span>) == <span class="number">2</span>              <span class="comment">//2</span></span><br><span class="line"><span class="keyword">assert</span> foo(<span class="number">1</span>, <span class="number">2</span>) == <span class="number">1</span>           <span class="comment">//3</span></span><br></pre></td></tr></table></figure><p>在这里只需要注意注释2即可，在注释2中我们传入的参数只有一个，但是两个重载的函数都符合我们的参数数量，这个时候Groovy会选择最为确切的方法来调用，两个方法中一个是可变参数，一个是接收一个参数，当然是后者更为确切了，所以后者被调用。因此遇到这种情况时，函数数量最符合的函数将会被调用。</p><h1 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h1><p>Groovy中的变量我们需要分成两类，一类叫做变量(Field)，一类叫做属性(Property)，尽管我们在JAVA中可以认为这两个其实就是一种东西，但是在Groovy中他们还是有稍微的区别的。</p><h2 id="变量-Field"><a href="#变量-Field" class="headerlink" title="变量(Field)"></a>变量(Field)</h2><ul><li>变量是可以使用访问修饰符的(如 public，protected，private)</li><li>可以使用零个或多个修饰符(如 static，final，synchronized)</li><li>类型是可选的，即可以声明类型也可以声明为无类型</li></ul><p>如下面的实例：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Data</span> &#123;</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> id =<span class="number">1</span>                                </span><br><span class="line">    <span class="keyword">protected</span> String description                    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> DEBUG = <span class="literal">false</span>       </span><br><span class="line">    <span class="keyword">private</span> mapping                       <span class="comment">//最好不要声明为无类型的</span></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String,String&gt; mapping1   <span class="comment">//最好在声明时加上确切的类型</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>尽管Groovy中支持在变量声明的时候声明为无类型，不过这并不是一个好的编程习惯，Groovy官方推荐我们最好在声明时指定类型。</p><h2 id="属性-Property"><a href="#属性-Property" class="headerlink" title="属性(Property)"></a>属性(Property)</h2><p>属性与变量有些许的区别，区别在于它不可以使用访问权限修饰符(public，protected，private)，它的声明格式应该如下：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> &#123;</span></span><br><span class="line">    String name                             </span><br><span class="line">    <span class="keyword">int</span> age                                 </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>Property与Field的区别在于，前者会默认为属性添加setter和getter方法，例如下面的这个例子就可以看出两者的区别：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> &#123;</span></span><br><span class="line">    <span class="keyword">public</span> String field             <span class="comment">//1</span></span><br><span class="line"></span><br><span class="line">    String property                 <span class="comment">//2</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* </span></span><br><span class="line"><span class="comment">         private String property</span></span><br><span class="line"><span class="comment">         public void setProperty(String property) &#123; ... &#125;</span></span><br><span class="line"><span class="comment">         public String getProperty() &#123; ... &#125;</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面的代码中注释1中创建了一个变量，那它就只是创建了一个变量而已；但是注释2中创建了一个属性，而它的作用则相当于接下来三行注释中的代码，即创建了一个private的同名的变量，以及public的setter与public的getter方法。尽管两种声明的方式原理不一样，但是我们访问这两者的方式却是可以一样的，如下所示:<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> a = <span class="keyword">new</span> A()</span><br><span class="line">println a.field             <span class="comment">//1</span></span><br><span class="line">println a.property        <span class="comment">//2</span></span><br></pre></td></tr></table></figure></p><p>上面的代码中第一行没有疑问，因为field变量是public的，但是第二行中property却是private，那为什么可以这样去访问到它呢？因为实际上当调用<code>a.property</code>时，调用的是<code>a.getProperty()</code>或者是<code>setProperty(String property)</code>。<br>下面再讲一个特殊的情况，就是使用final关键词来修饰的属性(property)是<code>只读</code>的，也就是说他只有getter方法没有setter方法，来看下面的实例：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> &#123;</span></span><br><span class="line">    <span class="keyword">final</span> String name                   </span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> age                       </span><br><span class="line">    Person(String name, <span class="keyword">int</span> age) &#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name                </span><br><span class="line">        <span class="keyword">this</span>.age = age                  </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> p = <span class="keyword">new</span> Person(<span class="string">'TOM'</span>,<span class="number">13</span>)</span><br><span class="line"><span class="keyword">assert</span> p.name == <span class="string">'TOM'</span>        <span class="comment">//1</span></span><br><span class="line">p.name=<span class="string">'TONY'</span>                 <span class="comment">//2</span></span><br></pre></td></tr></table></figure></p><p>上面的代码中注释1处中的<code>p.name</code>实际上是调用了p.getName()函数，但是注释2处却会报<code>groovy.lang.ReadOnlyPropertyException</code>异常，因为被final修饰的属性是不会生成setter方法的，因此当我们要设置这个变量的值时就会报错。</p><p>接下来最后一个需要注意的点是Groovy很贴心的一个功能，即只要我们按照Java Bean的规范声明了属性的getter和setter方法，那么我们就不需要再去声明这个变量了，我们就可以直接用<code>对象.属性</code>的方法来访问这个属性了，具体的方法我们看下面的例子：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">class PseudoProperties &#123;</span><br><span class="line">    // 属性&quot;name&quot;可读可写</span><br><span class="line">    void setName(String name) &#123;&#125;</span><br><span class="line">    String getName() &#123;&apos;Foo&apos;&#125;</span><br><span class="line"></span><br><span class="line">    // 属性&quot;age&quot;只可读</span><br><span class="line">    int getAge() &#123; 42 &#125;</span><br><span class="line"></span><br><span class="line">    // 属性&quot;groovy&quot;只可写</span><br><span class="line">    void setGroovy(boolean groovy) &#123;  &#125;</span><br><span class="line">&#125;</span><br><span class="line">def p = new PseudoProperties()</span><br><span class="line">p.name = &apos;Foo&apos;           //1    </span><br><span class="line">assert p.name == &apos;Foo&apos;   //2    </span><br><span class="line">assert p.age == 42       //3           </span><br><span class="line">p.groovy = true          //4</span><br></pre></td></tr></table></figure></p><p>上面的代码中其实声明了三个变量，只是这三个变量的访问权限不一样而已，在注释1处我们可以设置name的值，注释2可以访问name的值，但是我们却只能访问age的值并不能设置它的值，同理我们也只能设置groovy的值但是不能访问。</p><h2 id="变量的作用域"><a href="#变量的作用域" class="headerlink" title="变量的作用域"></a>变量的作用域</h2><p>在Groovy中变量是有作用域的，普通的Groovy类与JAVA类的变量作用域一致，但是有的时候我们也可以直接写Groovy脚本，即不用定义类直接写代码，这个时候的作用域就不太一样了。下面我们来看看实例：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> groovy.transform.Field   <span class="comment">//不导入则无法使用Field注解</span></span><br><span class="line"></span><br><span class="line">String hello = <span class="string">"hello"</span>          <span class="comment">//定义变量，作用域是本地域</span></span><br><span class="line"><span class="keyword">def</span> world = <span class="string">"world"</span>             <span class="comment">//定义变量，作用域是本地域</span></span><br><span class="line">helloworld = <span class="string">"hello world"</span>      <span class="comment">//全局变量，作用域是绑定域</span></span><br><span class="line"><span class="meta">@Field</span> a = <span class="string">'I am A'</span>             <span class="comment">//定义一个全局变量a</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> check() &#123;</span><br><span class="line">    println hello                         <span class="comment">//1报错</span></span><br><span class="line">    println world                         <span class="comment">//2报错</span></span><br><span class="line">    <span class="keyword">assert</span> helloworld == <span class="string">"hello world"</span>                         <span class="comment">//3</span></span><br><span class="line">    <span class="keyword">assert</span> a == <span class="string">"I am A"</span>                         <span class="comment">//4</span></span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">check()</span><br></pre></td></tr></table></figure></p><p>解释上面的代码之前我们需要了解一下在Groovy脚本中定义的变量有两种作用域，分别是<strong>绑定域(Binding Scope)</strong>和<strong>本地域(Local Scope)</strong>。<br><strong>本地域</strong><br>本地域是脚本中使用 def 定义的动态类型变量或用特定类型定义的静态类型变量的作用域。<br><strong>绑定域</strong><br>绑定域为没有任何前缀修饰的变量的作用域。<br>例如在上面的代码中，<code>hello</code>与<code>world</code>变量的作用域就是本地域，而<code>helloworld</code>的作用域为绑定域。<br>两者的区别是什么呢？因为Groovy脚本会被编译成一个类，类名为文件名，该类继承自<code>groovy.lang.Script</code>类，我们刚才讲到的<code>本地域</code>变量会被声明在该类的run()方法内部作为临时变量，它只能在定义它的代码块中被调用，而不能在其他代码块(其他方法)中被调用；绑定域其实也是被声明在run()方法内部的，但是他会被当做参数传递给调用它的方法，所以实际上的我们就可以理解为我们可以在其他方法中调用绑定域的变量。</p><p>通过上面的解释，我们可以清楚的看到上面的代码中注释1处和注释2处之所以会报错就是因为访问不到变量，而注释3则是可以访问到的。<br>还有最后一个就是注释4，首先a变量定义前先导入了Field注解，并在使用的时候显示的引用注解，这个时候a变量就变成了一个全局变量，这是因为a变量在编译之后被声明在了类的层级中，即我们在JAVA中所说的<code>全局变量</code>，既然是全局变量，那么所有的方法都是可以访问到他的。<br>这里需要注意的就是使用<code>@Field</code>注解与使用绑定域的结果尽管是一致的但是原理却是不同的，我们可以使用<code>jd-gui</code>这个可以将class字节码转换为java代码的软件查看上述代码编译之后的情况来验证我们上面的说法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">field_scope</span> <span class="keyword">extends</span> <span class="title">Script</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  Object a;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">field_scope</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    String str = <span class="string">"I am A"</span>;</span><br><span class="line">    <span class="keyword">this</span>.a = str;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">field_scope</span><span class="params">(Binding context)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(context);</span><br><span class="line">    String str = <span class="string">"I am A"</span>;</span><br><span class="line">    <span class="keyword">this</span>.a = str;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    CallSite[] arrayOfCallSite = $getCallSiteArray();</span><br><span class="line">    arrayOfCallSite[<span class="number">0</span>].call(InvokerHelper.class, field_scope.class, args);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> Object <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    CallSite[] arrayOfCallSite = $getCallSiteArray(); </span><br><span class="line">    String hello = <span class="string">"hello"</span>;</span><br><span class="line">    Object world = <span class="string">"world"</span>;</span><br><span class="line">    String str1 = <span class="string">"hello world"</span>; </span><br><span class="line">    ScriptBytecodeAdapter.setGroovyObjectProperty(str1, field_scope.class, <span class="keyword">this</span>, (String)<span class="string">"helloworld"</span>);</span><br><span class="line">    <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ((__$stMC) || (BytecodeInterface8.disabledStandardMetaClass())) </span><br><span class="line">    &#123; </span><br><span class="line">      <span class="keyword">return</span> arrayOfCallSite[<span class="number">1</span>].callCurrent(<span class="keyword">this</span>); </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">    &#123; </span><br><span class="line">      check(); </span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>; </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">check</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    CallSite[] arrayOfCallSite = $getCallSiteArray(); </span><br><span class="line">    arrayOfCallSite[<span class="number">2</span>].callCurrent(<span class="keyword">this</span>, arrayOfCallSite[<span class="number">3</span>].callGroovyObjectGetProperty(<span class="keyword">this</span>));</span><br><span class="line">    arrayOfCallSite[<span class="number">4</span>].callCurrent(<span class="keyword">this</span>, arrayOfCallSite[<span class="number">5</span>].callGroovyObjectGetProperty(<span class="keyword">this</span>));</span><br><span class="line">    ValueRecorder localValueRecorder1 = <span class="keyword">new</span> ValueRecorder();</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      Object tmp64_59 = arrayOfCallSite[<span class="number">6</span>].callGroovyObjectGetProperty(<span class="keyword">this</span>); </span><br><span class="line">      localValueRecorder1.record(tmp64_59, <span class="number">8</span>);</span><br><span class="line">      Object tmp73_64 = tmp64_59; </span><br><span class="line">      localValueRecorder1.record(tmp73_64, <span class="number">8</span>);</span><br><span class="line">      <span class="keyword">boolean</span> tmp87_84 = ScriptBytecodeAdapter.compareEqual(tmp73_64, <span class="string">"hello world"</span>); </span><br><span class="line">      localValueRecorder1.record(Boolean.valueOf(tmp87_84), <span class="number">19</span>); </span><br><span class="line">      <span class="keyword">if</span> (tmp87_84) </span><br><span class="line">        localValueRecorder1.clear(); </span><br><span class="line">      <span class="keyword">else</span> </span><br><span class="line">        ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render(<span class="string">"assert helloworld == \"hello world\"    //3"</span>, localValueRecorder1), <span class="keyword">null</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">finally</span> &#123; localValueRecorder1.clear(); <span class="keyword">throw</span> <span class="keyword">finally</span>; &#125;</span><br><span class="line">    ValueRecorder localValueRecorder2 = <span class="keyword">new</span> ValueRecorder();</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      Object tmp139_136 = <span class="keyword">this</span>.a; localValueRecorder2.record(tmp139_136, <span class="number">8</span>);</span><br><span class="line">      Object tmp148_139 = tmp139_136; localValueRecorder2.record(tmp148_139, <span class="number">8</span>);</span><br><span class="line">      <span class="keyword">boolean</span> tmp162_159 = ScriptBytecodeAdapter.compareEqual(tmp148_139, <span class="string">"I am A"</span>); </span><br><span class="line">      localValueRecorder2.record(Boolean.valueOf(tmp162_159), <span class="number">10</span>); </span><br><span class="line">      <span class="keyword">if</span> (tmp162_159) </span><br><span class="line">        localValueRecorder2.clear(); </span><br><span class="line">      <span class="keyword">else</span> </span><br><span class="line">        ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render(<span class="string">"assert a == \"I am A\"                  //4"</span>, localValueRecorder2), <span class="keyword">null</span>);  </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">finally</span> &#123; localValueRecorder2.clear(); <span class="keyword">throw</span> <span class="keyword">finally</span>;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="类"><a href="#类" class="headerlink" title="类"></a>类</h1><p>上面说到Groovy其实是可以不用定义类的，这也符合脚本语言的特性。不过如果是比较复杂的工程，为了能够系统的管理代码，我们还是需要为其定义类。定义类的方法与JAVA类似，但是不需要使用<code>public</code>关键字，因为在Groovy中默认就是public，不仅仅是类，方法与变量也是这样的。我们先用下面的代码来看看Groovy语言的特点。<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Language</span>&#123;</span></span><br><span class="line">    <span class="keyword">def</span> name</span><br><span class="line">    <span class="keyword">def</span> difficulty</span><br><span class="line">    </span><br><span class="line">    String toString()&#123;</span><br><span class="line">        <span class="string">"$name,$difficulty"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> java_lan =<span class="keyword">new</span> Language()</span><br><span class="line">java_lan.setName <span class="string">"JAVA"</span></span><br><span class="line">java_lan.difficulty = <span class="number">5</span></span><br><span class="line">println java_lan</span><br></pre></td></tr></table></figure></p><p>上面的代码相信大家现在是可以看得懂的啦，下面我们再讲一下关于Groovy中的类其他需要注意的事项：</p><ul><li>类中无须定义构造方法</li></ul><p>在Groovy的类中，其实有两个默认的构造方法，一个是无参的，一个是带有map参数的构造函数，所以实际上我们有什么函数只需要通过这个带有map参数的函数传递即可，请看下面的实例：</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> java_lan =<span class="keyword">new</span> Language()</span><br><span class="line">java_lan.setName <span class="string">"JAVA"</span></span><br><span class="line">java_lan.difficulty = <span class="number">5</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> groovy_lan = <span class="keyword">new</span> Language([<span class="string">"name"</span>:<span class="string">"Groovy"</span>,<span class="string">"difficulty"</span>:<span class="number">5</span>])</span><br></pre></td></tr></table></figure><ul><li>import的用法</li></ul><p>import语句与JAVA很相似，但是也增加了一些新的特点<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//包声明</span></span><br><span class="line"><span class="keyword">package</span> com.bob.groovy.test</span><br><span class="line"></span><br><span class="line"><span class="comment">//import语句1</span></span><br><span class="line"><span class="keyword">import</span> groovy.json.JsonBuilder</span><br><span class="line"><span class="keyword">def</span> json1 = <span class="keyword">new</span> JsonBuilder()</span><br><span class="line"></span><br><span class="line"><span class="comment">//import语句2</span></span><br><span class="line"><span class="keyword">import</span> groovy.json.*</span><br><span class="line"><span class="keyword">def</span> json2 = <span class="keyword">new</span> JsonBuilder()</span><br><span class="line"></span><br><span class="line"><span class="comment">//import语句3，使用as关键字取别名</span></span><br><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> Calendar.getInstance <span class="keyword">as</span> now</span><br><span class="line"><span class="keyword">assert</span> now().<span class="keyword">class</span> == Calendar.getInstance().<span class="keyword">class</span></span><br></pre></td></tr></table></figure></p><p>不过要特别注意，Groovy与Java类似，已经帮我们默认导入了一些常用的包，所以在我们使用这些包的类时就不用再像上面那样导入了，如下是自动导入的包列表：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.*</span><br><span class="line"><span class="keyword">import</span> java.util.*</span><br><span class="line"><span class="keyword">import</span> java.io.*</span><br><span class="line"><span class="keyword">import</span> java.net.*</span><br><span class="line"><span class="keyword">import</span> groovy.lang.*</span><br><span class="line"><span class="keyword">import</span> groovy.util.*</span><br><span class="line"><span class="keyword">import</span> java.math.BigInteger</span><br><span class="line"><span class="keyword">import</span> java.math.BigDecimal</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;http://wensibo.top/2018/02/07/gradle1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;上一篇文章&lt;/a&gt;我们从整体上初步认识了Gradle，也说了Gradle的学习曲线，这篇文章我们就按照顺序来讲解Groovy语言。&lt;br&gt;需要说明的是这篇文章大部分内容是翻译自&lt;a href=&quot;http://www.groovy-lang.org/syntax.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Groovy的官方文档&lt;/a&gt;，不过中间也有些许内容是我自己学习过程中总结整理的，如果大家英语好的话建议直接看英文文档。&lt;br&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2018-02-07Gradle1/gradle_logo.jpg&quot; alt=&quot;&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;&lt;/div&gt;
            &lt;/figure&gt;&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Gradle" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Gradle/"/>
    
    
      <category term="Gradle" scheme="http://www.wensibo.top/tags/Gradle/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Gradle（一）：初识Gradle</title>
    <link href="http://www.wensibo.top/2018/02/07/gradle1/"/>
    <id>http://www.wensibo.top/2018/02/07/gradle1/</id>
    <published>2018-02-07T04:07:25.000Z</published>
    <updated>2018-10-21T08:26:20.748Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>2018年的第一篇文章将会给大家介绍Gradle，这会是一个系列的文章，名字依旧是一口一口吃掉。<br>相信很多朋友都和我一样从eclipse转到Android Studio时是又爱又恨，爱是因为Android Studio功能强大，UI也很炫酷，当然还有Google爸爸支持；恨是因为从比较熟悉的eclipse一下子转到了Android Studio，本以为速度会快很多，然而现实却给了自己一记大耳光，其次就是Android Studio采用了Gradle来对项目进行编译，但是Gradle是个啥鬼咯，配置文件又看不懂，首次编译还得下载，下载速度贼慢，不fq就gg。<br>Android Studio现在已经更新到3.0.1.0，如果你对Gradle还不是很熟悉，那这个系列的文章将会帮到你。相信仔细看完整个系列之后你将会对如何在Android开发中使用Gradle得心应手。祝你好运，我们出发吧！<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2018-02-07Gradle1/gradle_logo.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><br><a id="more"></a> </p><h1 id="何为Gradle"><a href="#何为Gradle" class="headerlink" title="何为Gradle"></a>何为Gradle</h1><p>在了解Gradle之前，我们来讲讲在Gradle之前我们构建Android程序的时候使用的最多的是Ant以及Maven，我们简单看看三者有什么区别。</p><h2 id="Ant"><a href="#Ant" class="headerlink" title="Ant"></a>Ant</h2><p>Ant是在2000年的时候发布的，因其简单易学的特点很快就成为了十分流行的Java项目的构建工具。<br>Ant的优点就是： </p><ul><li>简单、易学，不需要什么特殊准备就能上手 </li><li>基于过程式编程思想使得构建非常灵活 </li></ul><p>不足之处就是使用XML作为脚本配置格式，如果项目比较大的话，XML文件将会很难管理。</p><h2 id="Maven"><a href="#Maven" class="headerlink" title="Maven"></a>Maven</h2><p>Maven是在2004年的时候发布的，他的出现在一定程度上解决了Ant的一些缺点。但是Maven同样沿用了Ant的做法：即把XML作为构建配置的文件格式，不过文件结构却有了巨大的变化：</p><ul><li>Ant需要开发者将执行task所需的全部命令都列出来</li><li>而Maven依靠约定并提供现成的可调用的目标</li><li>不仅如此，Maven更重要的一个进步是具备从网络上自动下载依赖的能力（当然Ant后来通过Ivy 也具备了这个功能）。</li></ul><p>Maven的缺点就是：</p><ul><li>依赖管理不能很好地处理相同库文件不同版本之间的冲突（Ivy在这方面更好一些）</li><li>XML作为配置文件的格式有严格的结构层次和标准，定制化目标很困难</li></ul><h2 id="Gradle"><a href="#Gradle" class="headerlink" title="Gradle"></a>Gradle</h2><p>Gradle则是在2012年发布的，它吸收了前两者的优点并极大的改进了他们的缺点，使其成为了Google官方推荐的构建语言。Gradle最大的改进就是摒弃了XML而是使用了一门基于java的Groovy语言作为构建脚本的书写语言，使得原本采用XML、冗杂得难以维护的配置文件变得清爽强大许多。</p><p>如果你还想对三者的对比有一个更直观的印象，可以查看<a href="http://blog.csdn.net/napolunyishi/article/details/39345995" target="_blank" rel="noopener">这篇文章</a>了解更详细的内容。这篇文章介绍了完成同一任务，三个构建工具的代码如下：<br><strong>Ant的ivy.xml文件</strong><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ivy-module</span> <span class="attr">version</span>=<span class="string">"2.0"</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">info</span> <span class="attr">organisation</span>=<span class="string">"org.apache"</span> <span class="attr">module</span>=<span class="string">"java-build-tools"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span> <span class="attr">org</span>=<span class="string">"junit"</span> <span class="attr">name</span>=<span class="string">"junit"</span> <span class="attr">rev</span>=<span class="string">"4.11"</span>/&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span> <span class="attr">org</span>=<span class="string">"org.hamcrest"</span> <span class="attr">name</span>=<span class="string">"hamcrest-all"</span> <span class="attr">rev</span>=<span class="string">"1.3"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">ivy-module</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>Ant的build.xml文件</strong><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns:ivy</span>=<span class="string">"antlib:org.apache.ivy.ant"</span> <span class="attr">name</span>=<span class="string">"java-build-tools"</span> <span class="attr">default</span>=<span class="string">"jar"</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"src.dir"</span> <span class="attr">value</span>=<span class="string">"src"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"build.dir"</span> <span class="attr">value</span>=<span class="string">"build"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"classes.dir"</span> <span class="attr">value</span>=<span class="string">"$&#123;build.dir&#125;/classes"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"jar.dir"</span> <span class="attr">value</span>=<span class="string">"$&#123;build.dir&#125;/jar"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"lib.dir"</span> <span class="attr">value</span>=<span class="string">"lib"</span> /&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">path</span> <span class="attr">id</span>=<span class="string">"lib.path.id"</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">fileset</span> <span class="attr">dir</span>=<span class="string">"$&#123;lib.dir&#125;"</span> /&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">path</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">target</span> <span class="attr">name</span>=<span class="string">"resolve"</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">ivy:retrieve</span> /&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">target</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">target</span> <span class="attr">name</span>=<span class="string">"clean"</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">delete</span> <span class="attr">dir</span>=<span class="string">"$&#123;build.dir&#125;"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">target</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">target</span> <span class="attr">name</span>=<span class="string">"compile"</span> <span class="attr">depends</span>=<span class="string">"resolve"</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">mkdir</span> <span class="attr">dir</span>=<span class="string">"$&#123;classes.dir&#125;"</span>/&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">javac</span> <span class="attr">srcdir</span>=<span class="string">"$&#123;src.dir&#125;"</span> <span class="attr">destdir</span>=<span class="string">"$&#123;classes.dir&#125;"</span> <span class="attr">classpathref</span>=<span class="string">"lib.path.id"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">target</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">target</span> <span class="attr">name</span>=<span class="string">"jar"</span> <span class="attr">depends</span>=<span class="string">"compile"</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">mkdir</span> <span class="attr">dir</span>=<span class="string">"$&#123;jar.dir&#125;"</span>/&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">jar</span> <span class="attr">destfile</span>=<span class="string">"$&#123;jar.dir&#125;/$&#123;ant.project.name&#125;.jar"</span> <span class="attr">basedir</span>=<span class="string">"$&#123;classes.dir&#125;"</span>/&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">target</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>Maven的pom.xml文件</strong><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span>  </span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span>  </span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.technologyconversations<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>java-build-tools<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.11<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.hamcrest<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hamcrest-all<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span>  </span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span>  </span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span>  </span><br><span class="line">                <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.3.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  </span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span>  </span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span>  </span><br><span class="line">   </span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>Gradle的build.gradle文件则只需要如下代码即可</strong><br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">apply <span class="string">plugin:</span> <span class="string">'java'</span>  </span><br><span class="line">apply <span class="string">plugin:</span> <span class="string">'checkstyle'</span>  </span><br><span class="line">apply <span class="string">plugin:</span> <span class="string">'findbugs'</span>  </span><br><span class="line">apply <span class="string">plugin:</span> <span class="string">'pmd'</span>  </span><br><span class="line">   </span><br><span class="line">version = <span class="string">'1.0'</span>  </span><br><span class="line">   </span><br><span class="line">repositories &#123;  </span><br><span class="line">    mavenCentral()  </span><br><span class="line">&#125;  </span><br><span class="line">   </span><br><span class="line">dependencies &#123;  </span><br><span class="line">    testCompile <span class="string">group:</span> <span class="string">'junit'</span>, <span class="string">name:</span> <span class="string">'junit'</span>, <span class="string">version:</span> <span class="string">'4.11'</span>  </span><br><span class="line">    testCompile <span class="string">group:</span> <span class="string">'org.hamcrest'</span>, <span class="string">name:</span> <span class="string">'hamcrest-all'</span>, <span class="string">version:</span> <span class="string">'1.3'</span>  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>单单从代码量上做一个选择的话，相信大家都会选择Gradle吧！</p><h1 id="Gradle有啥作用"><a href="#Gradle有啥作用" class="headerlink" title="Gradle有啥作用"></a>Gradle有啥作用</h1><p>我们一直在说Gradle是一个构建工具，可是构建(build)究竟是个啥意思呢？我们以Android平台为例，构建就是将我们的代码，资源文件进行打包，编译最后形成一个可以安装运行的apk文件的过程。如果你还不是很了解，那么我们来瞧瞧Google官方给出的Android应用程序构建的完整流程图吧！<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2018-02-07Gradle1/android_build_process.png" alt="典型 Android 应用模块的构建流程" title="">                </div>                <div class="image-caption">典型 Android 应用模块的构建流程</div>            </figure><br>上述的流程主要包含以下几个过程：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">将程序中的以及引用的第三方库中的资源文件、代码文件、和aidl文件通过java编译器编译成.class文件，再通过dex工具将.class文件转换为.dex文件。</span><br><span class="line">使用apkbuilder工具将所有未编译的资源（如图片），已编译的资源和.dex文件打包到.apk文件中。</span><br><span class="line">jarsigner工具使用调试密钥或正式发行版本的秘钥将.apk文件签名。</span><br><span class="line">使用zipalign工具对齐.apk包中的文件，以减少其在设备上运行时的内存占用。</span><br></pre></td></tr></table></figure></p><p>以上的每个步骤就是构建工具需要完成的，也就是说这其中的许多事情都是由Gradle来干的。所以说没有Gradle那就需要我们手动一个步骤一个步骤做，有了构建工具，程序员只需要动动手指就可以啦！</p><h1 id="搭建Gradle开发环境"><a href="#搭建Gradle开发环境" class="headerlink" title="搭建Gradle开发环境"></a>搭建Gradle开发环境</h1><p>工欲善其事,必先利其器，想要了解并且使用Gradle，我们得先把开发环境搭建起来。</p><h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>下载的方法有很多种，这里介绍的是适用于各个平台的，首先需要到<a href="http://services.gradle.org/distributions/" target="_blank" rel="noopener">Gradle官网</a>，然后找到你想要的版本下载即可。需要注意的是Gradle官网的服务器在国外，所以下载速度贼慢，最好是能够自备梯子，否则你需要耐心等待一段时间。<br>在这个页面中你会看到同一个版本号也有几个版本，如：-src对应的是Gradle源代码，这个不是我们需要下载的；-bin对应的是二进制版本，但不包含源代码，文档等，体积相对较小；-all版本是完全版，既包含了编译需要的二进制文件，也包含了源代码和开发文档等。<br>推荐大家一开始可以下载一个-all版本作为你主要使用的版本，如果你需要使用到其他版本号的，再去下载其他版本号对应的-bin版本，这样可以节省磁盘空间。当然如果你只安装了-bin版本，你可以到<a href="https://docs.gradle.org/4.4.1/javadoc/" target="_blank" rel="noopener">这里</a>查看在线的官方文档，在<a href="https://github.com/gradle/gradle" target="_blank" rel="noopener">这里</a>查看Gradle的源代码。</p><h2 id="解压-amp-配置环境变量"><a href="#解压-amp-配置环境变量" class="headerlink" title="解压&amp;配置环境变量"></a>解压&amp;配置环境变量</h2><p>下载完成之后需要解压，以-all版本为例，解压之后的目录结构如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2018-02-07Gradle1/gradle-all_dir.png" alt="gradle-4.1-all目录结构图" title="">                </div>                <div class="image-caption">gradle-4.1-all目录结构图</div>            </figure><br>接着就是配置环境变量<br><strong>Linux系统下</strong><br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/profile</span><br><span class="line"><span class="meta">#</span># 将"/usr/local/gradle-4.3"替换为你解压好的gradle的根目录</span><br><span class="line">export GRADLE_HOME=/usr/local/gradle-4.3</span><br><span class="line">PATH=$PATH:$GRADLE_HOME/bin</span><br><span class="line">source /etc/profile</span><br></pre></td></tr></table></figure></p><p><strong>Windows系统下</strong><br>在环境变量配置页面新增一个系统变量GRADLE_HOME，值为你解压好的gradle的根目录；接着修改Path变量，在最后追加<code>%GRADLE_HOME%\bin;</code>，之后点击应用，确定即可。</p><p>配置好之后需要测试一下Gradle的开发环境是否搭建成功，在linux下打开终端，在Windows下则是打开命令行，输入<code>gradle -v</code>然后回车，如果输出的信息与下面类似那就说明Gradle的开发环境已经搭建成功啦！</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">bob@bob-PC:~$ gradle -v</span><br><span class="line">------------------------------------------------------------</span><br><span class="line">Gradle 4.3</span><br><span class="line">------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">Build time:   2017-10-30 15:43:29 UTC</span><br><span class="line">Revision:     c684c202534c4138b51033b52d871939b8d38d72</span><br><span class="line"></span><br><span class="line">Groovy:       2.4.12</span><br><span class="line">Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015</span><br><span class="line">JVM:          1.8.0_131 (Oracle Corporation 25.131-b11)</span><br><span class="line">OS:           Linux 4.9.0-deepin4-amd64 amd64</span><br></pre></td></tr></table></figure><h1 id="写一个Hello-World的小demo"><a href="#写一个Hello-World的小demo" class="headerlink" title="写一个Hello World的小demo"></a>写一个Hello World的小demo</h1><p>前面我们说到Gradle是基于Groovy的，所以我们还得再学Groovy，下面的这个例子我们就先来领略一下Groovy，具体的语法我们将在下一篇文章继续学习。<br>首先我们新建一个目录，然后在目录下新建一个build.gradle文件，接着用记事本或者sublime打开，写入如下代码：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">task hello&#123;</span><br><span class="line">    println <span class="string">'Hello World'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>之后保存，之后打开终端(Windows使用命令行)进入当前目录(Windows可以直接在当前目录下同时按住<code>Shift</code>键和鼠标右键选择命令行或者Powershell打开则可以直接进入当前目录)，输入如下代码：<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gradle -q hello</span><br></pre></td></tr></table></figure></p><p>不出意外的话屏幕上会打印Hello World。</p><p>我们再来回顾一下整个过程，首先是新建一个build.gradle文件，有没有很熟悉呢？在Android项目中，也经常会出现build.gradle文件，在工程中他扮演着十分重要的角色，现在只需要知道它是整个项目的入口即可，具体的我们后续再讲。<br>接着我们再来看看在build.gradle文件中插入的代码是什么意思:<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">task hello&#123;</span><br><span class="line">    println <span class="string">'Hello World'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>首先声明了一个task(任务)，这个任务的名称叫做hello，接着使用<code>{}</code>将该任务的内容包围起来，即花括号里面的内容就是这个hello任务的内容，我们看到他的输出语句与java十分相似，但是也有不同点：</p><ul><li>java中方法的声明和调用需要使用<code>()</code>，但是groovy则可以不用，这样可以更加简洁。</li><li>java使用双引号来表示字符串，但是在groovy中则直接使用了单引号，这是因为在groovy的语法中，字符串的表达方式比较多种，有的需要单引号<code>&#39;&#39;</code>，有的需要双引号<code>&quot;&quot;</code>，还有的需要三引号<code>&#39;&#39;&#39;&#39;&#39;&#39;</code>，每种符号都有他使用的场景与作用，这个我们也是放到后面再讲。</li><li>java需要使用分号来表示一行语句的结束，而groovy与kotlin一样不需要使用分号来作为语句的结束标志，只需要换行即可。<br>最后我们使用命令行输入的<code>gradle -q hello</code>又是代表什么意思呢？<br>首先gradle则是命令，它会去查找当前所在目录下的build.gradle文件，因为这个文件就是整个命令的入口，而hello就是我们刚才命名的任务的名称，那么-q又是什么呢？其实这里的-q代表quiet模式，它不会生成 Gradle的日志信息(log messages)，所以用户只能看到tasks的输出，这样可以让输出信息更加清晰。</li></ul><p>通过上面的说明大家应该都能够理解。</p><h1 id="Gradle-Wrapper"><a href="#Gradle-Wrapper" class="headerlink" title="Gradle Wrapper"></a>Gradle Wrapper</h1><p>相信大家在运行github上的项目时经常会出现导入失败的问题，例如我们将一个项目导入了Android studio，但是由于项目使用的gradle版本与我们本机安装的不一致，尽管我们可以手动更改配置文件使得该项目使用我们本机的gradle，不过不同的gradle版本对应用程序的编译支持又不一样，所以最好就是还得自行下载gradle然后再运行，这就是为什么我们在Android studio倒入一个项目有的时候需要很久的原因。为了应对着一些列复杂的操作，Google推出了gradle wrapper(即gradle包装器)。接下来我们来看看gradle wrapper是如何解决的。</p><h2 id="首先程序的开发者需要为工程提供gradle-wrapper的支持"><a href="#首先程序的开发者需要为工程提供gradle-wrapper的支持" class="headerlink" title="首先程序的开发者需要为工程提供gradle wrapper的支持"></a>首先程序的开发者需要为工程提供gradle wrapper的支持</h2><p>我们使用者想要使用gradle wrapper来构建项目，得需要项目的开发者为该工程添加gradle wrapper的支持，这样我们要使用的时候才知道需要哪个版本的gradle来构建项目。为工程添加gradle wrapper其实很简单，一般来说有两种方法。</p><h3 id="使用终端-或命令行-添加gradle-wrapper"><a href="#使用终端-或命令行-添加gradle-wrapper" class="headerlink" title="使用终端(或命令行)添加gradle wrapper"></a>使用终端(或命令行)添加gradle wrapper</h3><p>在项目的根目录下打开终端或者命令行并进入到该目录，接着使用如下的命令即可：<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span># 将下方4.0换成你需要的gradle版本号，将all换成你需要的版本类型，例如bin</span><br><span class="line">bob@bob-PC:~/workspace/gradle_workspace/how_to_use_gradleWrapper$ gradle wrapper --gradle-version 4.0 --distribution-type all</span><br><span class="line"></span><br><span class="line">BUILD SUCCESSFUL in 9s</span><br><span class="line">1 actionable task: 1 executed</span><br></pre></td></tr></table></figure></p><p>如果出现了上方的构建成功的信息则代表已经成功为工程添加了gradle wrapper。</p><h3 id="编写文件添加gradle-wrapper"><a href="#编写文件添加gradle-wrapper" class="headerlink" title="编写文件添加gradle wrapper"></a>编写文件添加gradle wrapper</h3><p>我们还可以通过编辑项目根目录的build.gradle文件，为该文件添加如下代码：<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">task wrapper(<span class="string">type:</span> Wrapper) &#123;</span><br><span class="line">    gradleVersion <span class="string">'4.0'</span></span><br><span class="line">    distributionType <span class="string">'all'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们可以看到其实就是添加了一个名为wrapper的任务，该任务还接受一个Wrapper类型的参数，然后在任务内部可以定义gradle的版本号，以及版本类型，根据我们的需要自行修改即可。<br>添加完该任务之后还是打开终端或者命令行进入项目的根目录执行wrapper任务：<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">bob@bob-PC:~/workspace/gradle_workspace/how_to_use_gradleWrapper$ gradle wrapper</span><br><span class="line"></span><br><span class="line">BUILD SUCCESSFUL in 0s</span><br><span class="line">1 actionable task: 1 executed</span><br></pre></td></tr></table></figure></p><p>通过这种方式我们也是可以为项目添加gradle wrapper的。</p><p>添加了gradle wrapper之后项目的根目录下添加了如下几个文件：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── build.gradle</span><br><span class="line">├── gradle</span><br><span class="line">│   └── wrapper</span><br><span class="line">│       ├── gradle-wrapper.jar</span><br><span class="line">│       └── gradle-wrapper.properties</span><br><span class="line">├── gradlew</span><br><span class="line">└── gradlew.bat</span><br></pre></td></tr></table></figure></p><p>新增加的四个文件各有含义：</p><ul><li>gradlew：linux下执行gradle wrapper的脚本</li><li>gradlew.bat：windows下执行gradle wrapper的脚本</li><li>gradle-wrapper.jar：gradle wrapper微类库，包含下载和解包gradle运行时的逻辑</li><li>gradle-wrapper.properties：gradle wrapper元信息，包含已下载gradle运行时的存储位置和原始URL</li></ul><h2 id="使用gradle-wrapper构建项目"><a href="#使用gradle-wrapper构建项目" class="headerlink" title="使用gradle wrapper构建项目"></a>使用gradle wrapper构建项目</h2><p>现在我们使用者已经拿到了支持gradle wrapper的项目，我们只需要打开终端或者命令行并导航到项目的根目录然后执行：<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span># linux系统下</span><br><span class="line">./gradlew build</span><br><span class="line"><span class="meta">#</span># Windows系统下</span><br><span class="line">gradlew build</span><br></pre></td></tr></table></figure></p><p>即可编译项目。这里的<code>gradlew</code>就是<code>gradle wrapper</code>的缩写了。当我们使用gradlew运行gradle时，他会首先检查wrapper对应的gradle是否可用，如果可用，他会将gradlew的所有参数委托给gradle，然后执行构建，如果gradle包不可用，它就会首先下载对应版本的gradle，然后再构建。</p><h2 id="下载很慢怎么办？"><a href="#下载很慢怎么办？" class="headerlink" title="下载很慢怎么办？"></a>下载很慢怎么办？</h2><p>如果执行了上述的命令之后发现gradle的下载很慢，那我们可以手动修改<code>gradle-wrapper.properties</code>文件，将其中的distributionUrl更改成下面这样，这样他就会复用我们本地的gradle而不是去服务器下载。<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span># linux系统下：</span><br><span class="line">distributionUrl='file:/home/bob/gradle/gradle-4.0-all.zip'</span><br><span class="line"><span class="meta">#</span># Windows系统下：</span><br><span class="line">distributionUrl='file:///D:/grale/gradle-4.0-all.zip'</span><br></pre></td></tr></table></figure></p><h1 id="Gradle该如何学习"><a href="#Gradle该如何学习" class="headerlink" title="Gradle该如何学习"></a>Gradle该如何学习</h1><p>通过上面的学习想必大家对gradle应该有了初步的认识了，但是gradle的知识还有很多，那我们要怎么学呢？</p><ul><li>首先gradle是基于groovy语言的，所以我们需要对groovy的语法有所了解，我们将在下一节与大家一起学习。</li><li>接着就是深入到Android项目中，来看看gradle的文件结构。</li><li>随后需要了解的就是gradle的插件，这些插件有的是专门用于Android项目的构建的，所以我们需要结合文档一起来理解。</li><li>再后面的话我们还得了解如何构建变体，例如我们需要为一个项目编译多个不同应用市场的渠道包。<br>当然还有很多，这里只是先给大家一个思路，之后的文章我们一起继续学习。</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;2018年的第一篇文章将会给大家介绍Gradle，这会是一个系列的文章，名字依旧是一口一口吃掉。&lt;br&gt;相信很多朋友都和我一样从eclipse转到Android Studio时是又爱又恨，爱是因为Android Studio功能强大，UI也很炫酷，当然还有Google爸爸支持；恨是因为从比较熟悉的eclipse一下子转到了Android Studio，本以为速度会快很多，然而现实却给了自己一记大耳光，其次就是Android Studio采用了Gradle来对项目进行编译，但是Gradle是个啥鬼咯，配置文件又看不懂，首次编译还得下载，下载速度贼慢，不fq就gg。&lt;br&gt;Android Studio现在已经更新到3.0.1.0，如果你对Gradle还不是很熟悉，那这个系列的文章将会帮到你。相信仔细看完整个系列之后你将会对如何在Android开发中使用Gradle得心应手。祝你好运，我们出发吧！&lt;br&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2018-02-07Gradle1/gradle_logo.jpg&quot; alt=&quot;&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;&lt;/div&gt;
            &lt;/figure&gt;&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Gradle" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Gradle/"/>
    
    
      <category term="Gradle" scheme="http://www.wensibo.top/tags/Gradle/"/>
    
  </entry>
  
  <entry>
    <title>2017年终总结</title>
    <link href="http://www.wensibo.top/2017/12/31/2017summary/"/>
    <id>http://www.wensibo.top/2017/12/31/2017summary/</id>
    <published>2017-12-31T08:07:25.000Z</published>
    <updated>2018-10-21T08:26:20.472Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>大家好，不知不觉已经有两个月的时间没有更新文章了，时间真是过得好快啊，转眼间2017也已经来到了最后一天，这篇文章就来记录和总结一下过去的这个2017年吧！<br>过去的两个月时间里状态不是很好，加上学校大四了还一大堆课，一大堆作业要做。本以为拿了offer就可以轻松看书学习了，想不到这段时间过得比找工作还累。本来计划的文章都没有写，拖延症太重了，不过最主要的原因还是自己的惰性太严重了，希望在接下来的新的一年里能有所改善吧！<br>2017年对我来说注定是不简单的一个年份，有收获有遗憾，收获的是有了人生中第一份工作、在开源世界上有了自己更多的贡献、阅读了许多经典的书籍让自己的知识的深度和广度上了一个台阶、当然也结识了许多志同道合优秀的朋友、同时趁着自己大学时光的最后一年也出去走走看看；当然遗憾也不少，总感觉时间不够用，也总感觉自己浪费了许多宝贵的年华，每每想到这些心头总是唏嘘不已。<br><a id="more"></a> </p><h1 id="关于实习和工作"><a href="#关于实习和工作" class="headerlink" title="关于实习和工作"></a>关于实习和工作</h1><p>今年一共有两次实习的经历，一个较长一个较短，较长的是在博雅，较短的是在CVTE，两家公司的同事和导师们都十分的nice，在这两段实习的时间里真的能够体会到商业项目与个人项目巨大的区别。前一段实习经历我记录在了<a href="http://wensibo.top/2017/08/31/trainee/" target="_blank" rel="noopener">这篇文章</a>中，后一段实习则因为自己的拖延症迟迟没有写成文章，不过在这短短的一个星期的考核实习中我的导师以及领导对我的帮助确实是巨大的，尤其是在对工程框架、编码规范、设计模式以及性能优化方面，很感谢他们给我提出的意见。当然我觉得在实习中也有一个比较好的建议想与大家一起分享的，其实这对正式工作也是有很大帮助的，那就是在每天早上工作前先列一个任务清单，确定自己一天需要完成的任务，这能够让你对一天的工作量有所准备，同时也让你更有计划性；接着是一天工作完之后最好能花点时间总结一下今天的任务完成的如何，同时记录自己工作过程中的收货以及遇到的困难等等，我觉得这个方法对我这个菜鸟确实帮助挺大的。<br>关于工作，我之前也写过一篇文章：<a href="http://wensibo.top/2017/08/31/trainee/" target="_blank" rel="noopener">一个三非渣本的安卓秋招之路</a> ，不过当初在写文章的时候还没有确定正式签约的公司，最后我选择了珍爱网，希望在这里能够遇见美好。</p><h1 id="关于开源"><a href="#关于开源" class="headerlink" title="关于开源"></a>关于开源</h1><p>这一年我开始逐步的拥抱开源，在开源世界上吸收了许多前辈们贡献的成果和经验，当然在此基础上我也向开源世界开始贡献我的绵薄之力。</p><h2 id="Github"><a href="#Github" class="headerlink" title="Github"></a><a href="https://github.com/Wensibob" target="_blank" rel="noopener">Github</a></h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/contribution.png" alt="贡献图" title="">                </div>                <div class="image-caption">贡献图</div>            </figure><p>上面这张图是我2017年总共的贡献图，实在惭愧，上半年完成的还是可以，但是到了下半年因为要实习还有找工作再加之拖延症一片惨不忍睹的白啊。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/repositories.png" alt="repositories" title="">                </div>                <div class="image-caption">repositories</div>            </figure><br>这一年一共写了9个项目，但是真正意义上的只有4个，其中start数量比较多的是<a href="https://github.com/Wensibob/GankClient" target="_blank" rel="noopener">GankClient</a>这个项目,我也把这个APP上架到了酷安酷市场了，目前的下载量是1992，下面两张图展示了<a href="https://www.coolapk.com/apk/138516" target="_blank" rel="noopener">干货集中营</a>的下载量和评论情况。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/gank_server.png" alt="gank" title="">                </div>                <div class="image-caption">gank</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/gank_client.png" alt="gank" title="">                </div>                <div class="image-caption">gank</div>            </figure><br>当然干货集中营这个APP还是需要感谢<a href="http://gank.io/" target="_blank" rel="noopener">代码家</a>的支持，以及许多写过干货集中营客户端的前辈们，我也是在向你们借鉴学习的过程中完成的。</p><h1 id="关于博客"><a href="#关于博客" class="headerlink" title="关于博客"></a>关于博客</h1><p>2017年1月份开始搭建的独立个人博客，在这一年的时间里网站总访客数：22725，网站总访问量：59067<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/blog_client.png" alt="网站访问量" title="">                </div>                <div class="image-caption">网站访问量</div>            </figure><br>在这一年里时间里，真的很感谢大家的支持，同时博客文章的内容质量不断的提升，博客内容应用层逐步转向系统层面，今后要与大家分享的则会是关于底层方面的知识。全年一共写了23篇文章，加上这篇就是24篇，但是从9月份开始更新频率就下了许多，原本是计划每个月2-3篇文章的，来年加油啦！<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/blog_archives.png" alt="博客内容归档" title="">                </div>                <div class="image-caption">博客内容归档</div>            </figure><br>当然除了在自己的博客上写之外，我还同步在<a href="https://juejin.im/user/589d35738d6d81006c85e7c9" target="_blank" rel="noopener">掘金</a>，<a href="https://www.jianshu.com/u/4628daeba600" target="_blank" rel="noopener">简书</a>，<a href="http://www.cnblogs.com/ghylzwsb/" target="_blank" rel="noopener">博客园</a>这三个第三方网站上，当然我会更喜欢掘金一些，因为在掘金上分享技术更为纯粹，并且它也只分享技术，今天偶然查看自己的主页，原来已经有188人关注我啦，获得的start数量也有761个，文章的阅读数量为17459。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/juejin.png" alt="掘金" title="">                </div>                <div class="image-caption">掘金</div>            </figure></p><h1 id="看书"><a href="#看书" class="headerlink" title="看书"></a>看书</h1><p>今年看的书不少，下面这张图是我认真看并且觉得不错的书。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/books.jpg" alt="books" title="">                </div>                <div class="image-caption">books</div>            </figure></p><p>还有几本书有的借人了，有的放在图书馆没有拿回来没有拍照，列在下面供大家参考。</p><ul><li>Android进阶之光</li><li>Android开发艺术探索</li><li>高性能Android应用开发</li><li>网络是怎样连接的</li><li>算法（第4版）</li><li>程序员的算法趣题</li><li>Java编程思想（第四版）</li><li>Gradle for Android (PDF)<br>其中《鸟哥的私房菜》、《Java编程思想》没有全部看完，只看了其中的一部分。讲真今年买书花了还挺多钱的，但是如果对自己有所帮助还是很愿意投资的，并且也知道作者出书不容易，应该支持正版啦！</li></ul><h1 id="朋友"><a href="#朋友" class="headerlink" title="朋友"></a>朋友</h1><p>写博客确实认识了许多志同道合的朋友，这里不能一一举例实在抱歉，向大家推荐两个我比较熟悉的大神，两位都还是学生，<a href="https://www.jianshu.com/u/383970bef0a0" target="_blank" rel="noopener">@Carson_Ho</a>是研究生，<a href="https://tonnyl.github.io/" target="_blank" rel="noopener">@Tonny</a>则跟我一样也是大四，两位大神比我厉害多了，我也经常看他们的博客和开源项目，<a href="https://www.jianshu.com/u/383970bef0a0" target="_blank" rel="noopener">@Carson_Ho</a>博客的更新频率真的很赞，一个月都有几篇，这一点真的很棒啊，希望以后我也能向他这样。</p><h1 id="世界那么大我想去走走"><a href="#世界那么大我想去走走" class="headerlink" title="世界那么大我想去走走"></a>世界那么大我想去走走</h1><p>大学最后一年，也想利用着最后的一点时间出去走走看看，于是就去了成都和重庆，讲真我可能是假的广东人，并不觉得四川和重庆的很辣，难道是我吃了假的辣椒？<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/chengdu.jpg" alt="成都" title="">                </div>                <div class="image-caption">成都</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-12-31summary/chongqing.jpg" alt="重庆" title="">                </div>                <div class="image-caption">重庆</div>            </figure></p><h1 id="遗憾"><a href="#遗憾" class="headerlink" title="遗憾"></a>遗憾</h1><p>2017年遗憾的事情还是挺多的，究其原因还是自己有的时候太懒惰了，没有一直坚持下去，无论是写博客还是开源项目，到了下半年都有所松懈。再加之我也很容易被琐碎的事情打扰，也因为如此很多事情都中途而费了，希望来年自己能够在这方面有所改进。</p><h1 id="2018年展望"><a href="#2018年展望" class="headerlink" title="2018年展望"></a>2018年展望</h1><p>2018年自己将迎来工作的第一个年头，希望自己能够尽快适应新的工作环境，在工作上能够有所建树。当然在技术学习的道路上也应用永无止境，自己也已经开始准备学习人工智能和深度学习方面的内容，技术人唯有不断学习才不会太快被淘汰。2018年博客将会继续更新，接下来将会写一个有关Gradle的系列文章，到后面还会与大家一起分享更多流行的技术；文章的更新频率方面会比今年做的更好，在工作学习之余会抽出更多的时间来更新文章。<br>最后祝大家2018年新年快乐，新的一年大家都能心想事成！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;大家好，不知不觉已经有两个月的时间没有更新文章了，时间真是过得好快啊，转眼间2017也已经来到了最后一天，这篇文章就来记录和总结一下过去的这个2017年吧！&lt;br&gt;过去的两个月时间里状态不是很好，加上学校大四了还一大堆课，一大堆作业要做。本以为拿了offer就可以轻松看书学习了，想不到这段时间过得比找工作还累。本来计划的文章都没有写，拖延症太重了，不过最主要的原因还是自己的惰性太严重了，希望在接下来的新的一年里能有所改善吧！&lt;br&gt;2017年对我来说注定是不简单的一个年份，有收获有遗憾，收获的是有了人生中第一份工作、在开源世界上有了自己更多的贡献、阅读了许多经典的书籍让自己的知识的深度和广度上了一个台阶、当然也结识了许多志同道合优秀的朋友、同时趁着自己大学时光的最后一年也出去走走看看；当然遗憾也不少，总感觉时间不够用，也总感觉自己浪费了许多宝贵的年华，每每想到这些心头总是唏嘘不已。&lt;br&gt;
    
    </summary>
    
      <category term="年终总结" scheme="http://www.wensibo.top/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/"/>
    
    
      <category term="总结" scheme="http://www.wensibo.top/tags/%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>一个三非渣本的安卓秋招之路</title>
    <link href="http://www.wensibo.top/2017/10/29/interview/"/>
    <id>http://www.wensibo.top/2017/10/29/interview/</id>
    <published>2017-10-29T08:23:20.000Z</published>
    <updated>2018-10-21T08:26:20.816Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>距离上次更新文章已经过去一个多月了，实在是很抱歉没有按照进度更新博客。最近主要是在忙秋招，前几天也刚刚结束，所以这篇文章就来和大家一起分享我的秋招之路。<br>或许大部分朋友都是从这篇文章————<a href="http://wensibo.top/2017/04/13/2017Tencent_review/" target="_blank" rel="noopener">2017腾讯实习生Android客户端开发面试总结</a>开始认识我的吧，在那篇文章中我也讲到自己是非科班出身，同时学校也是非985非211的普通一本学校，这也就是标题中讲到的“三非”，这篇文章主要是记录一下我的秋招历程，在文章中我也会分享自己准备秋招的一些经验，希望对大家有所帮助。话不多说让我们开始吧！<br><a id="more"></a></p><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>先来说一下我目前的情况吧，截止到这篇文章发布的时候，我手上是拿到了4个offer，分别是实习的公司，珍爱网，久邦数码，租租车，在等待最终结果的是还有1个。像我这种实力是拿不到大厂offer的啦，请大神们轻喷。<br>回顾整个秋招，从8月底开始，先后总共投了11份简历，8份过了笔试来到了面试，在这8个中有2个一面挂分别是腾讯和4399，剩下6个都到了终面，最后有1个终面挂，剩下5个就是上面说到的5个。很多朋友会问为什么只投了11份呢？因为个人的关系，我选择在广东省内工作，其实主要就是广州和深圳，再加上本身投递的岗位是Android开发，所以选择的公司就少了很多了，其实我也很羡慕那些能够到外省工作的同学，当然这个是个人自己的选择啦！下面我就来细讲一下这些面试的过程。</p><h1 id="面试总结"><a href="#面试总结" class="headerlink" title="面试总结"></a>面试总结</h1><h2 id="体验最好的面试————CVTE"><a href="#体验最好的面试————CVTE" class="headerlink" title="体验最好的面试————CVTE"></a>体验最好的面试————CVTE</h2><p>CVTE是我第一个去面试的公司，三轮面试下来总共是两天，不过两天不连续，这两天的面试都是到地铁站附近等候公司的班车过来，然后坐班车前往公司面试，一面二面安排在同一天，从等待面试的人数就可以看出CVTE很受欢迎。当天我是早上11点到的公司，然后工作人员安排我们到电影院稍作休息，等候面试通知，从公司的基础设施以及环境就可以看出CVTE还是挺有钱的，工作人员也十分的热情，整个面试流程也是尽然有序。</p><h3 id="CVTE一面"><a href="#CVTE一面" class="headerlink" title="CVTE一面"></a>CVTE一面</h3><p>一面的时候是两个面试官同时面试我，一个问问题的时候另一个做记录，另外一个问问题再由另一个做记录，这轮面试总共40分钟，面完之后感觉快要虚脱，因为面试节奏很快，如果脑袋不够用就可能当场gg，不过问的问题都是很基础的知识，只要基础够扎实基本没问题。下面是我被问到的一些问题：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">从ActivityA跳转到ActivityB的生命周期调用顺序？</span><br><span class="line">Activity的四种启动方式？</span><br><span class="line">Android多线程的通信方式有什么？做简要介绍。</span><br><span class="line">Android中的消息机制？</span><br><span class="line">HandlerThread的原理？</span><br><span class="line">Window的工作原理和作用？</span><br><span class="line">自定义View的流程以及需要注意哪些地方？</span><br><span class="line">自定义View的时候如何实现wrap_content属性？</span><br><span class="line">View事件分发机制？</span><br><span class="line">滑动冲突的解决方法有哪些？子View如何通知父View去拦截某事件？</span><br><span class="line">Volley原理？</span><br><span class="line">retrofit原理？</span><br><span class="line">JAVA的四种元注解是什么？主要作用有哪些？</span><br><span class="line">okhttp原理？</span><br><span class="line">老年代与新生代的区别？</span><br><span class="line">JVM中的复制算法是什么？</span><br><span class="line">java加锁机制有哪些方法？原理分别是什么？</span><br><span class="line">wait()和sleep()的区别是什么？</span><br><span class="line">synchronized关键字的四种用法和区别？</span><br><span class="line">onCopyWriteArrayList的原理？</span><br><span class="line">Hashmap实现原理？</span><br><span class="line">concurrentHashmap原理？</span><br><span class="line">反射机制原理？</span><br><span class="line">动态代理原理？</span><br><span class="line">单例模式有哪些实现方法？</span><br><span class="line">手写算法题————字符串反转的有哪些方法？</span><br></pre></td></tr></table></figure></p><p>一面大概历时40分钟，时间也来到12点多，结束之后就等待二面，因为怕面试官通知面试所以就没去吃午饭，话说CVTE的零食和饭堂都是挺不错的，可惜没有去尝一下。差不多一点多的时候就是二面。</p><h3 id="CVTE二面"><a href="#CVTE二面" class="headerlink" title="CVTE二面"></a>CVTE二面</h3><p>二面应该是总监面，问题主要偏向项目，有一些问题已经忘记了，只能列出一些还记得的：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">做了哪些项目？详细说一下项目。</span><br><span class="line">讲一下RXJava原理；subscribeOn()与observeOn()哪一个方法多次切换线程只有第一次有效？为什么？</span><br><span class="line">Volley原理？</span><br><span class="line">Retrofit原理？</span><br><span class="line">Volley与Retrofit缓存机制有哪些区别？</span><br><span class="line">简单工厂，工厂方法，抽象工厂三种设计模式的优缺点？</span><br><span class="line">什么是内存泄露，什么是内存溢出？</span><br><span class="line">平常使用什么分析工具来优化APP的性能？</span><br><span class="line">手写算法题————实现一个栈</span><br><span class="line">Java的集合类包括哪些？各自有什么特点？</span><br><span class="line">为什么写博客？</span><br><span class="line">今后想要往哪些方面发展？</span><br></pre></td></tr></table></figure></p><p>二面其实发挥得不好，尤其是讲到RxJava的时候，因为没有仔细看RxJava源码，后续会写关于这个开源库源码解析的文章。<br>过了几天之后面试状态变成了终面，然后就前往CVTE的第二产业园进行HR面，一般是两个面试官同时面三个同学，不过因为我面的那一批人数不够，所以只是2对2。不得不说CVTE的HR真的很专业，把我从小到大的事情都扒出来了，问的问题大体如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">对自己两轮面试打一下分</span><br><span class="line">都面试了哪些公司，拿到哪些offer？</span><br><span class="line">小时候印象最深刻的一件？</span><br><span class="line">说说自己的家庭情况？</span><br><span class="line">说说对自己影响最大的人？</span><br><span class="line">父母的性格都是怎么样的？</span><br><span class="line">从小什么事情对自己的影响最大？</span><br><span class="line">期望的工作地点，每天期望的工作时间？一周期望工作多久？</span><br><span class="line">有什么理由会让你拒绝CVTE的offer？</span><br><span class="line">说说你对薪资的要求？</span><br><span class="line">觉得什么是自己不喜欢的？</span><br><span class="line">说一下自己的优点和缺点？</span><br><span class="line">梦想是什么？</span><br><span class="line">有什么问题想问我？</span><br></pre></td></tr></table></figure></p><p>全程还是挺严肃的，大概30分钟，不过因为之前对一些问题做了准备，所以没有遇到太大的难题。HR面之后过了几天官网显示已经通过终面，不过终面过了并不表示能够顺利拿到offer啦，按照CVTE的惯例，顺利通过终面的学生一般是需要经历一周的考核，考核的项目完成之后会进行再一轮的HR面，最后才会给结果。也就在前两天我刚刚结束了为期一周的实习，实习期间我的导师，同事，老大都很nice，有什么问题都可以向这些同事们请教，他们都会很耐心的帮忙解答，更重要的是CVTE给实习生的待遇很好，除了很好吃的一日三餐以及宵夜之外还会安排公司的公寓式酒店给实习生入住，相信去实习的同学对这点应该都很清楚。说了这么多好的那也该说一下不是特别好的地方，首先就是工作强度会稍微有点大，不过因为我是短期实习生，同时也要这3~4天内完成一个项目，所以压力相对大，每天几乎都是8点半左右上班工作到晚上9点多，中午12点多吃午饭，下午两点上班，如果你觉得这样的工作强度适应的了的话那来CVTE肯定是很好的选择，最后再说一下项目考核之后的HR面吧：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">实习的这一周有什么不适的吗？</span><br><span class="line">对公司的安排有什么意见吗？</span><br><span class="line">觉得哪方面还没有达到你的预期？</span><br><span class="line">跟家里人说了来实习的情况了吗？家里人都说了些什么？</span><br><span class="line">父母对你未来的期望是什么？</span><br><span class="line">未来对父母有什么打算？</span><br><span class="line">家里情况怎么样？</span><br><span class="line">父母对你的影响是什么？</span><br><span class="line">从小到大对你造成的负面影响最大的事情是什么？</span><br><span class="line">哪一段时间是自己压力最大的时候？</span><br><span class="line">给自己的实习考核打个分？</span><br><span class="line">觉得自己能不能够通过此次的考核？为什么？</span><br><span class="line">期望的薪资是多少？最低的薪资要求是多少？</span><br><span class="line">如果考核评估觉得你无法达到这样的薪资水平你该怎么办？</span><br><span class="line">如果没有通过CVTE的考核你该怎么办？</span><br><span class="line">手里都拿到哪些Offer了？薪资水平都怎么样？</span><br><span class="line">都拿到这么多offer了为什么还来实习？</span><br><span class="line">为什么会选择CVTE？</span><br><span class="line">有什么理由让你拒绝CVTE的offer？</span><br><span class="line">什么情况下你会从CVTE离职？</span><br><span class="line">还想对我们说什么？用一句话表达。</span><br></pre></td></tr></table></figure></p><p>讲真，经历了这么多次面试，CVTE的HR面给我的印象是最深刻的，当然这次的面试压力也是最大的，跟我一起面试的另一个小伙伴也顶着很大的压力，我们两个面试结束之后都大口的舒了口气，这次的HR是相对比较高层的，经验可以说相当丰富啊，对每个问题都会挖得很深直击要害，如果没有提前做好充分的准备可能会被问哭。总之这个星期的实习确实是个非常不错的经历。</p><h2 id="体验最糟的面试————4399"><a href="#体验最糟的面试————4399" class="headerlink" title="体验最糟的面试————4399"></a>体验最糟的面试————4399</h2><p>其实这样黑4399也不是很好，那就当成是我个人的片面之见吧，写出来只是与大家分享而已，完全没有恶意。4399的面试是在华工中心酒店，去之前就听说今年的面试有点水，一面似乎都是20分钟草草而过，听去面试的同学讲起问的技术问题不多，主要聊人生。本来也不是很想去的，因为同时间也赶着去另外一家公司面试，不过为了给自己多一次机会，所以也来到现场。话说4399的人气不是盖的，会议室里坐满了人，到了现场不到3分钟就被叫去一面。</p><h3 id="4399一面"><a href="#4399一面" class="headerlink" title="4399一面"></a>4399一面</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">自我介绍</span><br><span class="line">问了一下项目</span><br><span class="line">说说ListView与RecyclerView的区别</span><br><span class="line">说说以后的发展方向</span><br><span class="line">为什么写博客</span><br><span class="line">用过什么设计模式？问说需不需要详细说一下面试官说不用。</span><br><span class="line">有优点和缺点？</span><br><span class="line">觉得自己什么情况下会离职？</span><br><span class="line">怎么学习Android的？</span><br></pre></td></tr></table></figure><p>大概就这些问题了，面完之后面试官叫我出去等一下，然后前台的工作人员过了一阵子叫我先回去等消息，接着另一个工作人员叫我稍等一下，然后在电脑上处理了3分钟左右，接着另外一个工作人员又叫我去面二面，不过刚才那个工作人员说他在处理我的，又过了2分钟他叫我回去等消息了。听到这个回复就知道gg了。<br>总体评价一下这次的面试，感觉4399确实很多人想去，不过不知道今年是不是不怎么招人，感觉面试都很水，也有可能面试官觉得我很渣，然后随便问问过一下流程吧！不过我一直觉得面试是一个互相了解的过程，双方都应该尊重彼此，不然怎么体现这个公司的形象呢？</p><h2 id="最轻松幽默的面试————珍爱网"><a href="#最轻松幽默的面试————珍爱网" class="headerlink" title="最轻松幽默的面试————珍爱网"></a>最轻松幽默的面试————珍爱网</h2><p>珍爱网简历是在8月份就投了，10月13号的时候在华工进行了现场笔试，最后HR面的时候面试官跟我说其实我的笔试成绩有点低(尴尬脸)，当初做的时候觉得除了算法题之外都答得挺好的呀，可能有些题目考虑的不是很全面吧。现在想想觉得自己还是挺幸运的。当天就收到一面的短信，一面面试官给我留下的印象十分深刻，无论是开口的第一句话：“我们不用搞得那么严肃，轻松点就行(哈哈)”，还是面试过程中对我回答的不好的问题耐心的纠正，到最后离开的时候站起来跟我握手。整体给面试者的印象都十分深刻，相信有面过珍爱网Android的同学都会倍感亲切和轻松吧！</p><h3 id="珍爱网一面"><a href="#珍爱网一面" class="headerlink" title="珍爱网一面"></a>珍爱网一面</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">介绍一下做过的项目，面试官问我手机上有没有安装做过的APP，自己的APP当然得装在手机上啦，于是面试过程基本都是围绕着这个项目展开的。</span><br><span class="line">介绍一下RecyclerView的原理以及优点。</span><br><span class="line">为什么会选用Material Design？</span><br><span class="line">Android 5.0之后的版本和之前的版本有什么区别？</span><br><span class="line">DVM与ART的区别？(回答得不好)</span><br><span class="line">为什么要写博客？</span><br><span class="line">怎么学习Android的？</span><br><span class="line">项目中遇到过哪些困难？如何去解决的？</span><br><span class="line">MVP与MVC有什么区别？</span><br><span class="line">Custom-Tabs-Client是什么？(因为在项目中用到这个开源库)作用是什么？</span><br><span class="line">Handler机制？Looper原理？</span><br><span class="line">如果在主线程中同时创建10个Handler，会发生什么事？(回答得不好)</span><br><span class="line">HandlerThread实现原理？</span><br><span class="line">Activity的启动流程？</span><br><span class="line">ActivityThread的main方法主要做了哪些操作？</span><br><span class="line">对团队有什么要求吗？</span><br><span class="line">有哪些问题要问他的？(去面试之前使用了珍爱网的APP，发现有一个页面有点卡顿，然后使用AS的dump View Hierarchy for UI automator观察了这个Activity的实现组件是webview，但不是传统的webview，而是基于腾讯的X5内核，同时注意到这个页面需要连接网络，如果没有网络连接的话就无法显示内容，后来通过使用分析知道这个页面的卡顿有一部分原因是网络连接造成的。基于这个问题与面试官进行了一番讨论。)</span><br><span class="line">还有什么问题要问他的吗？(请教了一下面试官在珍爱网工作最大的收获是什么)</span><br></pre></td></tr></table></figure><p>面试结束之后面试官主动站起来跟我握手，我都有点受宠若惊了。分析了一下这次面试，觉得自己的技术问题上大部分答得都还行，不过有两个问题基本属于知识盲区，所以回答的不好。能过一面我觉得除了运气好之外，可能是我问的两个问题也加了不少分，第一个问题主要是我有去使用他们的产品，并且对其中的问题有自己的思考和深入的了解，这可能是面试官更加看中的吧；第二个问题能够体现出我很想去珍爱网的热情。</p><h3 id="珍爱网二面"><a href="#珍爱网二面" class="headerlink" title="珍爱网二面"></a>珍爱网二面</h3><p>二面应该是总监面或者总裁面吧，面试官懂技术，会问一些技术问题，估计是CTO之类的高管。主要的问题记录如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">介绍一下项目，面试官同样也拿着我的手机看项目。</span><br><span class="line">项目中有没有做一下性能的优化？优化工具有哪些？</span><br><span class="line">了解过哪些设计模式？分别详细说一下优缺点？</span><br><span class="line">什么情况下使用单例模式？</span><br><span class="line">java中的加锁有哪些方法？</span><br><span class="line">说一下网络连接的状态码的含义？</span><br><span class="line">说一下长连接的优点和缺点？</span><br><span class="line">如何看待Android未来的发展？</span><br><span class="line">为什么会选择学习Android？</span><br><span class="line">如何学习Android？</span><br><span class="line">自己有什么优势？</span><br><span class="line">为什么写博客？</span><br><span class="line">项目中遇到过哪些困难？如何去解决的？</span><br><span class="line">遇到过最大的坎是什么？</span><br><span class="line">有什么问题要问他的？(面试前看了一下珍爱网的官网，注意到对应届生有一个培养计划————纯珍计划，于是就向面试官提问)</span><br></pre></td></tr></table></figure></p><p>二面感觉发挥还可以，个人还是觉得问的问题还是比较重要的，要让面试官觉得你是一个有心的人，觉得你很想来公司工作，所以从官网了解公司是最直接的啦！二面紧接着就是HR面了，HR小姐姐很有趣，这一轮问的问题就是对一些基本问题的了解了，如实回答即可。过几天之后就收到offer通知了。</p><h2 id="最真诚的面试————租租车"><a href="#最真诚的面试————租租车" class="headerlink" title="最真诚的面试————租租车"></a>最真诚的面试————租租车</h2><p>这里说的最真诚当然不是说我自己啦，我对待每次面试都是十分真诚的啦，这里的真诚就是指租租车的面试官们，因为公司已经经过了B+融资，今年应该是首次校招，不过从规模上来看招聘会办的十分好，招的人数也不少。一面的面试官挺负责任的，在面试之前先浏览了我的博客，以下是面试的一些问题。</p><h3 id="租租车一面"><a href="#租租车一面" class="headerlink" title="租租车一面"></a>租租车一面</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">自我介绍</span><br><span class="line">介绍一下项目</span><br><span class="line">说一下Retrofit原理</span><br><span class="line">说一下java中4种元注解？</span><br><span class="line">说一下@Retention元注解的三个取值的使用范围和场景分别是什么？</span><br><span class="line">说一下Volley的原理</span><br><span class="line">说一下Retrofit与Volley的区别和使用场景</span><br><span class="line">说一下Android中的多线程通信机制</span><br><span class="line">说一下Android中的Binder机制原理</span><br><span class="line">自定义View的流程</span><br><span class="line">MeasureSpec是什么？</span><br><span class="line">View事件的分发机制</span><br><span class="line">Android如何实现图片的高效加载</span><br><span class="line">java中的四种引用以及使用场景</span><br><span class="line">项目中有做过性能优化吗？如何优化的?</span><br><span class="line">JVM的内存模型？</span><br><span class="line">java有什么垃圾回收算法？分别是怎么实现的？</span><br><span class="line">java多线程的三个特性？原理分别是什么？</span><br><span class="line">java多线程的锁机制有什么实现方式？原理分别是什么？</span><br><span class="line">说说java中的集合类？</span><br><span class="line">写一下单例模式？</span><br><span class="line">为什么静态内部类不会造成内存泄漏？</span><br><span class="line">说一下TCP与UDP的区别？</span><br><span class="line">说一下网络连接的三次握手和四次挥手？</span><br><span class="line">为什么写博客？</span><br><span class="line">写博客给你带来什么成长？</span><br><span class="line">有什么问题想问我的吗？(面试前使用了公司的APP，发现有一个页面出现了bug，然后就基于这个问题与面试官展开了讨论。)</span><br></pre></td></tr></table></figure><h3 id="租租车二面"><a href="#租租车二面" class="headerlink" title="租租车二面"></a>租租车二面</h3><p>租租车的二面是HR面，问的问题基本和其他的HR面相同，下面就列举一些还记得的<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">家是哪里的啊？父母对未来的工作有什么要求啊？</span><br><span class="line">有什么优缺点吗？</span><br><span class="line">为什么想要写博客呢？</span><br><span class="line">觉得自己性格怎么样？</span><br><span class="line">对未来的团队有什么要求吗？</span><br><span class="line">拿到哪些Offer了吗？</span><br><span class="line">对薪资方面有什么要求吗？</span><br><span class="line">对自己未来几年的打算是什么？</span><br><span class="line">学校什么时候发三方啊？</span><br><span class="line">还有什么问题要问我吗？</span><br></pre></td></tr></table></figure></p><p>租租车的工作环境是我比较喜欢的，地点在天河大厦，办公楼虽然只是一栋三四层的写字楼但都是自己的，而且装修十分讲究，从门外往里看就能明显感受到与其他公司的差别。两次面试加上一开始的笔试体验都挺不错的，去刚刚起步的初创公司确实能够学到很多当然对自己也是一种历练，更让我喜欢的一点则是公司员工大部分都是年轻人，跟他们工作会相对的轻松些。当然最后还是很开心能够顺利拿到租租车的offer啦！</p><h1 id="Android秋招如何准备？"><a href="#Android秋招如何准备？" class="headerlink" title="Android秋招如何准备？"></a>Android秋招如何准备？</h1><p>其实每个人都有自己特有的学习方法，我们都需要寻找到最适合自己的，在这里我只是列举出我复习的技巧，大家只需要选择适合自己的就行。</p><h2 id="书籍和网站"><a href="#书籍和网站" class="headerlink" title="书籍和网站"></a>书籍和网站</h2><p>我觉得看书真的是十分的重要，每一本书都有自己的知识体系，但是好书确实不多，需要经历一番挑选；关于网站则是对一些体系较小的知识进行汇总，这里推荐的是设计模式。以下我列举一些个人觉得不错的一些书籍和网站：</p><ul><li>Android 相关</li></ul><blockquote><ul><li>《Android开发艺术探索》</li><li>《深入理解Android虚拟机》</li><li>《Android源码设计模式解析与实战》</li></ul></blockquote><ul><li>Java相关</li></ul><blockquote><ul><li>《Java编程思想》</li><li>《深入理解Java虚拟机》</li><li>《图解Java多线程设计模式》</li></ul></blockquote><ul><li>网络相关</li></ul><blockquote><ul><li>《计算机网络》(谢希仁 编著)</li><li>《网络是怎么连接的》</li></ul></blockquote><ul><li>数据结构与算法相关</li></ul><blockquote><ul><li>《算法导论》</li><li>《算法》(第4版)</li></ul></blockquote><ul><li>设计模式相关</li></ul><blockquote><ul><li><a href="http://design-patterns.readthedocs.io/zh_CN/latest/index.html" target="_blank" rel="noopener">图说设计模式</a></li><li><a href="https://gof.quanke.name/" target="_blank" rel="noopener">设计模式Java版</a></li></ul></blockquote><ul><li>练习相关</li></ul><blockquote><ul><li><a href="https://www.nowcoder.com/5645772" target="_blank" rel="noopener">牛客网在线笔试题(多练练编程题)</a></li><li><a href="https://leetcode.com/" target="_blank" rel="noopener">LeetCode</a></li></ul></blockquote><h2 id="知识总结"><a href="#知识总结" class="headerlink" title="知识总结"></a>知识总结</h2><p>看了这么多书籍和网站之后需要对这些知识做一个比较系统的整理，这里推荐使用思维导图工具，不过市面上的这类工具已经足够多了，有的收费有的免(po)费(jie)，大家可以根据自己的使用习惯自行选择，我个人使用的是<a href="http://www.mindmanager.cc/" target="_blank" rel="noopener">mindjet</a>，下图则是我总结的Android复习体系图，完整的版本大家可以点击<a href="https://files.cnblogs.com/files/ghylzwsb/Android%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93.rar" target="_blank" rel="noopener">这里</a>下载。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-10-29Interview/mindmap.png" alt="Android面试知识体系" title="">                </div>                <div class="image-caption">Android面试知识体系</div>            </figure></p><h2 id="面试技巧"><a href="#面试技巧" class="headerlink" title="面试技巧"></a>面试技巧</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">面试前需要对自己总结的知识重新回顾一遍，以便在被问及时能够快速并且全面的回答问题。</span><br><span class="line">面试前对该公司稍做了解，可以从官网或者从产品等方面了解，如果有产品的话还可以使用，看看有没有存在一些bug，当面试官要你提问题的时候可以跟他一起讨论。</span><br><span class="line">面试完之后做稍做记录，记下自己回答的不好的问题，方便查漏补缺。</span><br></pre></td></tr></table></figure><h1 id="关于简历"><a href="#关于简历" class="headerlink" title="关于简历"></a>关于简历</h1><p>我觉得简历真的十分重要，如果你的学历和能力都不是很有优势的情况下那么简历很可能就决定了你是否有面试的机会了。当然我的简历做得也不是很好，在这里只是与大家一起分享我在准备简历过程中的一些收获，下面一一与大家分享。</p><h2 id="为每个公司准备一份简历"><a href="#为每个公司准备一份简历" class="headerlink" title="为每个公司准备一份简历"></a>为每个公司准备一份简历</h2><p>如果你不是去一些大型招聘会，不知道都有哪些企业过来招人，那么你大可以用一份普遍适用的简历投递出去，但如果你已经明确知道今天要去哪家公司面试了，那最好能够为这个企业准备一份专属的简历。最基本的就是在简历的页眉部分加上这个公司的logo，其次简历的文字主题色与logo相呼应，例如给腾讯的简历大概长这样<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-10-29Interview/tencent.jpg" alt="人丑不露相" title="">                </div>                <div class="image-caption">人丑不露相</div>            </figure></p><h2 id="要不要放照片？"><a href="#要不要放照片？" class="headerlink" title="要不要放照片？"></a>要不要放照片？</h2><p>首先需要声明的是技术岗一般不看颜值，如果你能力棒棒哒同时颜值高高哒，那基本上就稳了，但如果你跟我一样都是屌丝，那得分两种情况讨论咯。首先如果你愿意花钱的话，可以到靠谱点儿的相馆拍张好的证件照，记得跟老板要电子版哦；如果你不愿意花钱的话，那我建议你还是不要放照片吧。</p><h2 id="简历最好要彩印"><a href="#简历最好要彩印" class="headerlink" title="简历最好要彩印"></a>简历最好要彩印</h2><p>其实我在简历上还是很舍得花钱的，今年学校换了打印店，彩印一面需要两块钱，每次打印心里都在流血啊，不过想到要给面试官留下一个好的印象，还是忍下心了。对了打印一定要用比较硬一点的纸，不要像普通A4纸那样软哦，这样手感才好哟！还有最好还是单面彩印，如果你的简历不止一面那就打几张用订书机钉起来！</p><h2 id="推荐一些实用的工具和网站"><a href="#推荐一些实用的工具和网站" class="headerlink" title="推荐一些实用的工具和网站"></a>推荐一些实用的工具和网站</h2><p>这里并不是推荐大家什么简历模板，讲真我也没用过这些，对于技术岗的同学简历不需要花哨，只要条理清晰版面整洁即可，基本都是可以用我的(word)哥做。这里推荐的是一款获取图片颜色的工具，还记得刚才我说简历的文字主题色最好与logo相呼应，那就得知道logo的RGB属性值了，使用这款<a href="https://files.cnblogs.com/files/ghylzwsb/GetRGB.rar" target="_blank" rel="noopener">GetRGB</a>工具则可以让你轻松获取图片的RGB参数。使用方式见下图哦！<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-10-29Interview/getrgb.gif" alt="如何使用GetRGB" title="">                </div>                <div class="image-caption">如何使用GetRGB</div>            </figure><br>另外推荐的则是一个图标库网站——<a href="http://www.iconfont.cn/" target="_blank" rel="noopener">阿里巴巴矢量图标库</a>，在上面几乎可以找到你所需要的图标，并且可以自定义颜色与大小，还是免费的哦！以后妈妈都不用担心我找不到图标了。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-10-29Interview/icon.PNG" alt="图标示例" title="">                </div>                <div class="image-caption">图标示例</div>            </figure></p><h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>作为一个Android本科渣渣，在秋招中虽然也经历过失利与彷徨，但对最终的结果还是很满意的。这段时间很感谢身边人对我的帮助，感谢我实习的领导给我的意见和帮助，感谢面试过程中遇到的所有面试官以及结识的朋友们，也感谢我的老师同学朋友一直以来对我的帮助，当然也要感谢我爸妈对我的培养，最后的感谢留给自己，感谢自己学习Android一路来的坚持和努力。希望这篇文章对你有用，同时也祝大家能够实现自己的目标找到心仪的工作！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;距离上次更新文章已经过去一个多月了，实在是很抱歉没有按照进度更新博客。最近主要是在忙秋招，前几天也刚刚结束，所以这篇文章就来和大家一起分享我的秋招之路。&lt;br&gt;或许大部分朋友都是从这篇文章————&lt;a href=&quot;http://wensibo.top/2017/04/13/2017Tencent_review/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2017腾讯实习生Android客户端开发面试总结&lt;/a&gt;开始认识我的吧，在那篇文章中我也讲到自己是非科班出身，同时学校也是非985非211的普通一本学校，这也就是标题中讲到的“三非”，这篇文章主要是记录一下我的秋招历程，在文章中我也会分享自己准备秋招的一些经验，希望对大家有所帮助。话不多说让我们开始吧！&lt;br&gt;
    
    </summary>
    
      <category term="秋招面经" scheme="http://www.wensibo.top/categories/%E7%A7%8B%E6%8B%9B%E9%9D%A2%E7%BB%8F/"/>
    
    
      <category term="面经" scheme="http://www.wensibo.top/tags/%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>Retrofit2.0源码解析</title>
    <link href="http://www.wensibo.top/2017/09/05/retrofit/"/>
    <id>http://www.wensibo.top/2017/09/05/retrofit/</id>
    <published>2017-09-05T02:20:20.000Z</published>
    <updated>2018-10-21T08:26:20.848Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>今天是九月的第四天了，学校也正式开学，趁着大学最后一年的这大好时光，抓紧时间赶快学习新知识吧！今天想要与大家一起分享的是Retrofit，由于网上已经有许多讲解Retrofit使用的文章了，本篇文章只会给一个小小的示例，以这个示例作为入口分析其源码，同样也会贴上流程图，以免迷路。话不多说，我们开始吧！！！<br><a id="more"></a></p><h2 id="关于Retrofit"><a href="#关于Retrofit" class="headerlink" title="关于Retrofit"></a>关于Retrofit</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>Retrofit是近来十分火热的一个网络请求开源库，Android开发者使用的网络请求开源库从最早的HttpClient与HttpURLConnection到2013年Google官方推出的Volley，接着就到了现在很火的OKHttp，最后才到了Retrofit。网络请求开源库的演变也正是移动互联网下用户对网络需求的真实写照。有哪个用户不想使用APP的时候网络加载速度更快，更省流量，更加安全呢？也就是基于用户的这些需求，才有了许多开源库的不断迭代，而Retrofit可以说正是当下最适合开发者使用的网络请求开源库之一。<br>何出此言呢？首先它是由大名鼎鼎的square公司出品的，或许你不知道square公司，但你应该认识<a href="https://github.com/JakeWharton" target="_blank" rel="noopener">Jake Wharton</a>，不过他最近已经到谷歌去了，倘若你连他都不知道，那你应该使用过他开发的这些开源库：<code>OkHttp</code>,<code>picasso</code>,<code>butterknife</code>,<code>RxAndroid</code>等等，可以说Retrofit是由一个十分厉害的公司开发和维护的，所以你大可以放心地在你的项目中使用。</p><h3 id="什么场景下适合使用呢？"><a href="#什么场景下适合使用呢？" class="headerlink" title="什么场景下适合使用呢？"></a>什么场景下适合使用呢？</h3><p>尽管Retrofit十分强大，但是他却不一定适合所有场景，正所谓术业有专攻，我们也不必大材小用，如果是一些频繁但是访问量很小的网络请求，那么Volley就足以对付了，接下来我列举一下Retrofit普遍的使用场景。</p><ul><li>服务器后台遵循<code>RESTful API</code>的设计风格。如果你对这种风格不熟悉，建议你看看阮一峰大神的<a href="http://www.ruanyifeng.com/blog/2014/05/restful_api.html" target="_blank" rel="noopener">这篇文章</a>，或者向你的后台小伙伴请教一番。</li><li>项目中使用了RxJava。如果你的项目中使用了RxJava，那么使用Retrofit绝对会让你的开发效率翻倍。</li><li>项目中的网络数据请求量比较大。如果你的应用经常会有数据量比较大的网络请求，那么使用Retrofit也会很有效，因为Retrofit底层的实现是使用OKHttp，而OKHttp就是适用于这种场景的。</li></ul><p>如果你符合以上三种情况，当然是选择Retrofit啦！<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/choose.jpg" alt="Retrofit绿" title="">                </div>                <div class="image-caption">Retrofit绿</div>            </figure></p><h2 id="一个简单的栗子"><a href="#一个简单的栗子" class="headerlink" title="一个简单的栗子"></a>一个简单的栗子</h2><p>说了这么多，我们就通过下面这个栗子来看看他究竟好在哪里？<br><strong>需要说明的是：这个例子是用来获取<a href="http://gank.io/api/" target="_blank" rel="noopener">干货集中营API</a>上面的数据</strong><br><strong>1、首先定义一个常量用来描述要访问的服务器主机的地址</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GankConfig</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String HOST = <span class="string">"http://gank.io/api/"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>2、定义返回数据的bean类</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GankData</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> List&lt;String&gt; category;</span><br><span class="line">    <span class="keyword">public</span> Result results;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Result</span></span>&#123;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"Android"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; androidList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"休息视频"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; restVideoList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"iOS"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; iosList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"福利"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; meiZiList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"拓展资源"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; extendResourceList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"瞎推荐"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; suggestionList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"App"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; appList;</span><br><span class="line">            <span class="meta">@SerializedName</span>(<span class="string">"前端"</span>)</span><br><span class="line">            <span class="keyword">public</span> List&lt;Gank&gt; webList;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>3、定义要访问的接口</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GankRetrofit</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这里以获取指定日期的内容为例子</span></span><br><span class="line">    <span class="meta">@GET</span>(<span class="string">"day/&#123;year&#125;/&#123;month&#125;/&#123;day&#125;"</span>)</span><br><span class="line">    <span class="function">GankData <span class="title">getDailyData</span><span class="params">(@Path(<span class="string">"year"</span>)</span> <span class="keyword">int</span> year, @<span class="title">Path</span><span class="params">(<span class="string">"month"</span>)</span> <span class="keyword">int</span> month, @<span class="title">Path</span><span class="params">(<span class="string">"day"</span>)</span> <span class="keyword">int</span> day)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>4、用单例模式创建一个Retrofit客户端</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GankRetrofitClient</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> GankRetrofit gankRetrofit;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> Retrofit retrofit;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">GankRetrofitClient</span><span class="params">()</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">static</span>&#123;</span><br><span class="line">                Gson date_gson = <span class="keyword">new</span> GsonBuilder().setDateFormat(<span class="string">"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"</span>).create();</span><br><span class="line">                retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">                        .baseUrl(GankConfig.HOST)</span><br><span class="line">                        .addConverterFactory(GsonConverterFactory.create(date_gson))<span class="comment">//添加一个转换器，将gson数据转换为bean类</span></span><br><span class="line">                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())<span class="comment">//添加一个适配器，与RxJava配合使用</span></span><br><span class="line">                        .build();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> GankRetrofit <span class="title">getGankRetrofitInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (gankRetrofit==<span class="keyword">null</span>)&#123;</span><br><span class="line">                        <span class="keyword">synchronized</span> (GankRetrofitClient.class)&#123;</span><br><span class="line">                                <span class="keyword">if</span> (gankRetrofit==<span class="keyword">null</span>)&#123;</span><br><span class="line">                                        gankRetrofit=retrofit.create(GankRetrofit.class);</span><br><span class="line">                                &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> gankRetrofit;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>5、使用Retrofit进行网络请求</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GankData data= GankRetrofitClient.getGankRetrofitInstance().getDailyData(<span class="number">2017</span>, <span class="number">9</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure></p><h2 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h2><h3 id="从Builder模式创建实例开始看起"><a href="#从Builder模式创建实例开始看起" class="headerlink" title="从Builder模式创建实例开始看起"></a>从Builder模式创建实例开始看起</h3><p>首先我们先从上面的第4步开始解析源码，有下面这段代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">            .baseUrl(GankConfig.HOST)</span><br><span class="line">            .addConverterFactory(GsonConverterFactory.create(date_gson))<span class="comment">//添加一个转换器，将gson数据转换为bean类</span></span><br><span class="line">            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())<span class="comment">//添加一个适配器，与RxJava配合使用</span></span><br><span class="line">            .build();</span><br></pre></td></tr></table></figure></p><p><strong>很明显这个是使用了Builder模式，接下来我们一步一步来看里面做了什么？首先是Builder()。</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Builder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>(Platform.get());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Builder(Platform platform) &#123;</span><br><span class="line">      <span class="keyword">this</span>.platform = platform;</span><br><span class="line">      <span class="comment">//添加转换器，请见下面关于addConverterFactory()的讲解</span></span><br><span class="line">      converterFactories.add(<span class="keyword">new</span> BuiltInConverters());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>构造方法中的参数是Platform的静态方法get()，接下来就看看get()。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Platform PLATFORM = findPlatform();</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">static</span> Platform <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> PLATFORM;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Platform <span class="title">findPlatform</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      Class.forName(<span class="string">"android.os.Build"</span>);</span><br><span class="line">      <span class="keyword">if</span> (Build.VERSION.SDK_INT != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Android();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (ClassNotFoundException ignored) &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      Class.forName(<span class="string">"java.util.Optional"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> Java8();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (ClassNotFoundException ignored) &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Platform();</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>可以看到，Retrofit支持多平台，包括Android与JAVA8，它会根据不同的平台设置不同的线程池。<br>先来看看到目前为止我们分析到哪里了</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/builder1.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><p><strong>接下来看一下baseUrl()方法。</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Builder <span class="title">baseUrl</span><span class="params">(String baseUrl)</span> </span>&#123;</span><br><span class="line">      checkNotNull(baseUrl, <span class="string">"baseUrl == null"</span>);</span><br><span class="line">      HttpUrl httpUrl = HttpUrl.parse(baseUrl);</span><br><span class="line">      <span class="keyword">if</span> (httpUrl == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal URL: "</span> + baseUrl);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> baseUrl(httpUrl);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>很容易理解，baseUrl()是配置服务器的地址的，如果为空，那么就会抛出异常。</p><p><strong>接着是addConverterFactory()</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> List&lt;Converter.Factory&gt; converterFactories = <span class="keyword">new</span> ArrayList&lt;&gt;(); </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Builder <span class="title">addConverterFactory</span><span class="params">(Converter.Factory factory)</span> </span>&#123;</span><br><span class="line">      converterFactories.add(checkNotNull(factory, <span class="string">"factory == null"</span>));</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>大家是不是还记得刚才在Builder()方法初始化的时候，有这样一行代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">converterFactories.add(<span class="keyword">new</span> BuiltInConverters());</span><br></pre></td></tr></table></figure></p><p>可以看到，converterFactories在初始化的时候就已经添加了一个默认的Converter，那我们手动添加的这个GsonConverter是干什么用的呢？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">GsonConverterFactory</span> <span class="keyword">extends</span> <span class="title">Converter</span>.<span class="title">Factory</span> </span>&#123;</span><br><span class="line"> </span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> GsonConverterFactory <span class="title">create</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> create(<span class="keyword">new</span> Gson());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> GsonConverterFactory <span class="title">create</span><span class="params">(Gson gson)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> GsonConverterFactory(gson);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Gson gson;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="title">GsonConverterFactory</span><span class="params">(Gson gson)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (gson == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"gson == null"</span>);</span><br><span class="line">    <span class="keyword">this</span>.gson = gson;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> Converter&lt;ResponseBody, ?&gt; responseBodyConverter(Type type, Annotation[] annotations,</span><br><span class="line">      Retrofit retrofit) &#123;</span><br><span class="line">    TypeAdapter&lt;?&gt; adapter = gson.getAdapter(TypeToken.get(type));</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> GsonResponseBodyConverter&lt;&gt;(gson, adapter);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> Converter&lt;?, RequestBody&gt; requestBodyConverter(Type type,</span><br><span class="line">      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) &#123;</span><br><span class="line">    TypeAdapter&lt;?&gt; adapter = gson.getAdapter(TypeToken.get(type));</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> GsonRequestBodyConverter&lt;&gt;(gson, adapter);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>其实这个Converter主要的作用就是将HTTP返回的数据解析成Java对象，我们常见的网络传输数据有Xml、Gson、protobuf等等，而GsonConverter就是将Gson数据转换为我们的Java对象，而不用我们重新去解析这些Gson数据。</p><p><strong>接着看addCallAdapterFactory()</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> List&lt;CallAdapter.Factory&gt; adapterFactories = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Builder <span class="title">addCallAdapterFactory</span><span class="params">(CallAdapter.Factory factory)</span> </span>&#123;</span><br><span class="line">      adapterFactories.add(checkNotNull(factory, <span class="string">"factory == null"</span>));</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到，CallAdapter同样也被一个List维护，也就是说用户可以添加多个CallAdapter，那Retrofit总得有一个默认的吧，默认的是什么呢？请看接下来的build()。</p><p><strong>最后看一下build()</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Retrofit <span class="title">build</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="comment">//检验baseUrl</span></span><br><span class="line">  <span class="keyword">if</span> (baseUrl == <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Base URL required."</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//创建一个call，默认情况下使用okhttp作为网络请求器</span></span><br><span class="line">  okhttp3.Call.Factory callFactory = <span class="keyword">this</span>.callFactory;</span><br><span class="line">  <span class="keyword">if</span> (callFactory == <span class="keyword">null</span>) &#123;</span><br><span class="line">    callFactory = <span class="keyword">new</span> OkHttpClient();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Executor callbackExecutor = <span class="keyword">this</span>.callbackExecutor;</span><br><span class="line">  <span class="keyword">if</span> (callbackExecutor == <span class="keyword">null</span>) &#123;</span><br><span class="line">    callbackExecutor = platform.defaultCallbackExecutor();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  List&lt;CallAdapter.Factory&gt; adapterFactories = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="keyword">this</span>.adapterFactories);</span><br><span class="line">  <span class="comment">//添加一个默认的callAdapter</span></span><br><span class="line">  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));</span><br><span class="line"></span><br><span class="line">  List&lt;Converter.Factory&gt; converterFactories = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="keyword">this</span>.converterFactories);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,</span><br><span class="line">      callbackExecutor, validateEagerly);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>首先Retrofit会新建一个call，其实质就是OKHttp，作用就是网络请求器；接着在上一点中我们困惑的callAdapter也已经能够得到解决了，首先Retrofit有一个默认的callAdapter，请看下面这段代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line">adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));</span><br><span class="line"></span><br><span class="line">CallAdapter.<span class="function">Factory <span class="title">defaultCallAdapterFactory</span><span class="params">(Executor callbackExecutor)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (callbackExecutor != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> ExecutorCallAdapterFactory(callbackExecutor);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> DefaultCallAdapterFactory.INSTANCE;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ExecutorCallAdapterFactory</span> <span class="keyword">extends</span> <span class="title">CallAdapter</span>.<span class="title">Factory</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> Executor callbackExecutor;</span><br><span class="line"></span><br><span class="line">  ExecutorCallAdapterFactory(Executor callbackExecutor) &#123;</span><br><span class="line">    <span class="keyword">this</span>.callbackExecutor = callbackExecutor;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> CallAdapter&lt;?, ?&gt; get(Type returnType, Annotation[] annotations, Retrofit retrofit) &#123;</span><br><span class="line">    <span class="keyword">if</span> (getRawType(returnType) != Call.class) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> Type responseType = Utils.getCallResponseType(returnType);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> CallAdapter&lt;Object, Call&lt;?&gt;&gt;() &#123;</span><br><span class="line">      <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Type <span class="title">responseType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> responseType;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Call&lt;Object&gt; <span class="title">adapt</span><span class="params">(Call&lt;Object&gt; call)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ExecutorCallbackCall&lt;&gt;(callbackExecutor, call);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ExecutorCallbackCall</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Call</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Executor callbackExecutor;</span><br><span class="line">    <span class="keyword">final</span> Call&lt;T&gt; delegate;</span><br><span class="line"></span><br><span class="line">    ExecutorCallbackCall(Executor callbackExecutor, Call&lt;T&gt; delegate) &#123;</span><br><span class="line">      <span class="keyword">this</span>.callbackExecutor = callbackExecutor;</span><br><span class="line">      <span class="keyword">this</span>.delegate = delegate;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">enqueue</span><span class="params">(<span class="keyword">final</span> Callback&lt;T&gt; callback)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (callback == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"callback == null"</span>);</span><br><span class="line"></span><br><span class="line">      delegate.enqueue(<span class="keyword">new</span> Callback&lt;T&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Call&lt;T&gt; call, <span class="keyword">final</span> Response&lt;T&gt; response)</span> </span>&#123;</span><br><span class="line">          callbackExecutor.execute(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">              <span class="keyword">if</span> (delegate.isCanceled()) &#123;</span><br><span class="line">                <span class="comment">// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.</span></span><br><span class="line">                callback.onFailure(ExecutorCallbackCall.<span class="keyword">this</span>, <span class="keyword">new</span> IOException(<span class="string">"Canceled"</span>));</span><br><span class="line">              &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                callback.onResponse(ExecutorCallbackCall.<span class="keyword">this</span>, response);</span><br><span class="line">              &#125;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onFailure</span><span class="params">(Call&lt;T&gt; call, <span class="keyword">final</span> Throwable t)</span> </span>&#123;</span><br><span class="line">          callbackExecutor.execute(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">              callback.onFailure(ExecutorCallbackCall.<span class="keyword">this</span>, t);</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isExecuted</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> delegate.isExecuted();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Response&lt;T&gt; <span class="title">execute</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> delegate.execute();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">cancel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      delegate.cancel();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isCanceled</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> delegate.isCanceled();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"CloneDoesntCallSuperClone"</span>) <span class="comment">// Performing deep clone.</span></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Call&lt;T&gt; <span class="title">clone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> ExecutorCallbackCall&lt;&gt;(callbackExecutor, delegate.clone());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Request <span class="title">request</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> delegate.request();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到默认的callAdapter是ExecutorCallAdapterFactory。callAdapter其实也是运用了适配器模式，其实质就是网络请求器Call的适配器，而在Retrofit中Call就是指OKHttp，那么CallAdapter就是用来将OKHttp适配给不同的平台的，在Retrofit中提供了四种CallAdapter，分别如下：</p><ul><li>ExecutorCallAdapterFactory（默认使用）</li><li>GuavaCallAdapterFactory</li><li>Java8CallAdapterFactory</li><li>RxJavaCallAdapterFactory</li></ul><p>为什么要提供如此多的适配器呢？首先是易于扩展，例如用户习惯使用什么适配器，只需要添加即可使用；再者RxJava如此火热，因为其切换线程十分的方便，不需要手动使用handler切换线程，而Retrofit使用了支持RxJava的适配器之后，功能也会更加强大。</p><p>综上我们已经将使用Builder模式创建出来的Retrofit实例分析完毕了，我们只需要对相关的功能进行配置即可，Retrofit负责接收我们配置的功能然后进行对象的初始化，这个也就是Builder模式屏蔽掉创建对象的复杂过程的好处。现在我们再次用流程图来梳理一下刚才的思路。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/builder2.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><h3 id="网络请求接口的创建"><a href="#网络请求接口的创建" class="headerlink" title="网络请求接口的创建"></a>网络请求接口的创建</h3><p>我最初使用Retrofit的时候觉得有一个地方十分神奇，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">GankRetrofit gankRetrofit=retrofit.create(GankRetrofit.class);</span><br><span class="line">GankData data= gankRetrofit.getDailyData(<span class="number">2017</span>, <span class="number">9</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure></p><p>要想解惑，首先得对动态代理有所了解，如果你对动态代理还不是很清楚，请点击<a href="http://blog.csdn.net/mhmyqn/article/details/48474815" target="_blank" rel="noopener">这里</a>了解动态代理的原理，之后再接着往下看。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/gaoneng.jpg" alt="前方高能预警" title="">                </div>                <div class="image-caption">前方高能预警</div>            </figure></p><p><strong>我们就以这里为切入点开始分析吧！首先是create()</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; <span class="function">T <span class="title">create</span><span class="params">(<span class="keyword">final</span> Class&lt;T&gt; service)</span> </span>&#123;</span><br><span class="line">    Utils.validateServiceInterface(service);</span><br><span class="line">    <span class="keyword">if</span> (validateEagerly) &#123;</span><br><span class="line">      eagerlyValidateMethods(service);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//重点看这里</span></span><br><span class="line">    <span class="keyword">return</span> (T) Proxy.newProxyInstance(service.getClassLoader(), <span class="keyword">new</span> Class&lt;?&gt;[] &#123; service &#125;,</span><br><span class="line">        <span class="keyword">new</span> InvocationHandler() &#123;</span><br><span class="line">          <span class="keyword">private</span> <span class="keyword">final</span> Platform platform = Platform.get();</span><br><span class="line"></span><br><span class="line">          <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span></span></span><br><span class="line"><span class="function">              <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">            <span class="comment">// If the method is a method from Object then defer to normal invocation.</span></span><br><span class="line">            <span class="keyword">if</span> (method.getDeclaringClass() == Object.class) &#123;</span><br><span class="line">              <span class="keyword">return</span> method.invoke(<span class="keyword">this</span>, args);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (platform.isDefaultMethod(method)) &#123;</span><br><span class="line">              <span class="keyword">return</span> platform.invokeDefaultMethod(method, service, proxy, args);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//下面就会讲到哦</span></span><br><span class="line">            ServiceMethod&lt;Object, Object&gt; serviceMethod =</span><br><span class="line">                (ServiceMethod&lt;Object, Object&gt;) loadServiceMethod(method);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//下一小节讲到哦</span></span><br><span class="line">            OkHttpCall&lt;Object&gt; okHttpCall = <span class="keyword">new</span> OkHttpCall&lt;&gt;(serviceMethod, args);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">//下两个小节讲哦</span></span><br><span class="line">            <span class="keyword">return</span> serviceMethod.callAdapter.adapt(okHttpCall);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>我们主要看Proxy.newProxyInstance方法，它接收三个参数，第一个是一个类加载器，其实哪个类的加载器都无所谓，这里为了方便就选择了我们所定义的借口的类加载器;第二个参数是我们定义的接口的class对象，第三个则是一个InvocationHandler匿名内部类。<br>那大家应该会有疑问了，这个newProxyInstance到底有什么用呢？其实他就是通过动态代理生成了网络请求接口的代理类，代理类生成之后，接下来我们就可以使用<code>ankRetrofit.getDailyData(2017, 9, 1);</code>这样的语句去调用getDailyData方法，当我们调用这个方法的时候就会被动态代理拦截，直接进入InvocationHandler的invoke方法。下面就来讲讲它。</p><p><strong>invoke方法</strong><br>它接收三个参数，第一个是动态代理，第二个是我们要调用的方法，这里就是指<code>getDailyData</code>，第三个是一个参数数组，同样的这里就是指<code>2017, 9, 1</code>，收到方法名和参数之后，紧接着会调用loadServiceMethod方法来生产过一个ServiceMethod对象，这里的一个ServiceMethod对象就对应我们在网络接口里定义的一个方法，相当于做了一层封装。接下来重点来看loadServiceMethod方法。</p><p><strong>loadServiceMethod方法</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">ServiceMethod&lt;?, ?&gt; loadServiceMethod(Method method) &#123;</span><br><span class="line">  ServiceMethod&lt;?, ?&gt; result = serviceMethodCache.get(method);</span><br><span class="line">  <span class="keyword">if</span> (result != <span class="keyword">null</span>) <span class="keyword">return</span> result;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">synchronized</span> (serviceMethodCache) &#123;</span><br><span class="line">    result = serviceMethodCache.get(method);</span><br><span class="line">    <span class="keyword">if</span> (result == <span class="keyword">null</span>) &#123;</span><br><span class="line">      result = <span class="keyword">new</span> ServiceMethod.Builder&lt;&gt;(<span class="keyword">this</span>, method).build();</span><br><span class="line">      serviceMethodCache.put(method, result);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>它调用了ServiceMethod类，而ServiceMethod也使用了Builder模式，直接先看Builder方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Builder(Retrofit retrofit, Method method) &#123;</span><br><span class="line">  <span class="keyword">this</span>.retrofit = retrofit;</span><br><span class="line">  <span class="comment">//获取接口中的方法名</span></span><br><span class="line">  <span class="keyword">this</span>.method = method;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//获取方法里的注解</span></span><br><span class="line">  <span class="keyword">this</span>.methodAnnotations = method.getAnnotations();</span><br><span class="line"></span><br><span class="line">  <span class="comment">//获取方法里的参数类型 </span></span><br><span class="line">  <span class="keyword">this</span>.parameterTypes = method.getGenericParameterTypes();</span><br><span class="line"></span><br><span class="line">  <span class="comment">//获取接口方法里的注解内容 </span></span><br><span class="line">  <span class="keyword">this</span>.parameterAnnotationsArray = method.getParameterAnnotations();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>再来看build方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ServiceMethod <span class="title">build</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      callAdapter = createCallAdapter();</span><br><span class="line">      responseType = callAdapter.responseType();</span><br><span class="line">      <span class="keyword">if</span> (responseType == Response.class || responseType == okhttp3.Response.class) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"'"</span></span><br><span class="line">            + Utils.getRawType(responseType).getName()</span><br><span class="line">            + <span class="string">"' is not a valid response body type. Did you mean ResponseBody?"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      responseConverter = createResponseConverter();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">for</span> (Annotation annotation : methodAnnotations) &#123;</span><br><span class="line">        parseMethodAnnotation(annotation);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (httpMethod == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"HTTP method annotation is required (e.g., @GET, @POST, etc.)."</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!hasBody) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isMultipart) &#123;</span><br><span class="line">          <span class="keyword">throw</span> methodError(</span><br><span class="line">              <span class="string">"Multipart can only be specified on HTTP methods with request body (e.g., @POST)."</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (isFormEncoded) &#123;</span><br><span class="line">          <span class="keyword">throw</span> methodError(<span class="string">"FormUrlEncoded can only be specified on HTTP methods with "</span></span><br><span class="line">              + <span class="string">"request body (e.g., @POST)."</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">int</span> parameterCount = parameterAnnotationsArray.length;</span><br><span class="line">      parameterHandlers = <span class="keyword">new</span> ParameterHandler&lt;?&gt;[parameterCount];</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">int</span> p = <span class="number">0</span>; p &lt; parameterCount; p++) &#123;</span><br><span class="line">        Type parameterType = parameterTypes[p];</span><br><span class="line">        <span class="keyword">if</span> (Utils.hasUnresolvableType(parameterType)) &#123;</span><br><span class="line">          <span class="keyword">throw</span> parameterError(p, <span class="string">"Parameter type must not include a type variable or wildcard: %s"</span>,</span><br><span class="line">              parameterType);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];</span><br><span class="line">        <span class="keyword">if</span> (parameterAnnotations == <span class="keyword">null</span>) &#123;</span><br><span class="line">          <span class="keyword">throw</span> parameterError(p, <span class="string">"No Retrofit annotation found."</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (relativeUrl == <span class="keyword">null</span> &amp;&amp; !gotUrl) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"Missing either @%s URL or @Url parameter."</span>, httpMethod);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!isFormEncoded &amp;&amp; !isMultipart &amp;&amp; !hasBody &amp;&amp; gotBody) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"Non-body HTTP method cannot contain @Body."</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (isFormEncoded &amp;&amp; !gotField) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"Form-encoded method must contain at least one @Field."</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (isMultipart &amp;&amp; !gotPart) &#123;</span><br><span class="line">        <span class="keyword">throw</span> methodError(<span class="string">"Multipart method must contain at least one @Part."</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> ServiceMethod&lt;&gt;(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>代码稍微有点长，但是思路很清晰，主要的工作有<br>1、首先对注解的合法性进行检验，例如，HTTP的请求方法是GET还是POST，如果不是就会抛出异常；<br>2、根据方法的返回值类型和方法注解从Retrofit对象的的callAdapter列表和Converter列表中分别获取到该方法对应的callAdapter和Converter；<br>3、将传递进来的参数与注解封装在parameterHandlers中，为后面的网络请求做准备。</p><p>先用流程图梳理一下刚才的思路：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/create1.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><br>分析到这里，我们总算是明白了最初的两行代码原来干了这么多事情，J神真的是流弊啊！接下来我们就来看一下网络请求部分。</p><h3 id="使用OkHttpCall进行网络请求"><a href="#使用OkHttpCall进行网络请求" class="headerlink" title="使用OkHttpCall进行网络请求"></a>使用OkHttpCall进行网络请求</h3><p>回头看一下上一小节讲解create方法时我们有这一行代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OkHttpCall&lt;Object&gt; okHttpCall = <span class="keyword">new</span> OkHttpCall&lt;&gt;(serviceMethod, args);</span><br></pre></td></tr></table></figure></p><p>他将我们刚才得到的serviceMethod与我们实际传入的参数传递给了OkHttpCall，接下来就来瞧瞧这个类做了些什么？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">OkHttpCall</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Call</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> ServiceMethod&lt;T, ?&gt; serviceMethod;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Object[] args;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> canceled;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// All guarded by this.</span></span><br><span class="line">  <span class="keyword">private</span> okhttp3.Call rawCall;</span><br><span class="line">  <span class="keyword">private</span> Throwable creationFailure; <span class="comment">// Either a RuntimeException or IOException.</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">boolean</span> executed;</span><br><span class="line"></span><br><span class="line">  OkHttpCall(ServiceMethod&lt;T, ?&gt; serviceMethod, Object[] args) &#123;</span><br><span class="line">    <span class="keyword">this</span>.serviceMethod = serviceMethod;</span><br><span class="line">    <span class="keyword">this</span>.args = args;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>很可惜，我们好像没有看到比较有用的东西，只是将传进来的参数进行了赋值，那我们就接着看create方法中的最后一行吧！</p><h3 id="callAdapter的使用"><a href="#callAdapter的使用" class="headerlink" title="callAdapter的使用"></a>callAdapter的使用</h3><p>create方法的最后一行是这样的：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> serviceMethod.callAdapter.adapt(okHttpCall);</span><br></pre></td></tr></table></figure></p><p>最后是调用了callAdapter的adapt方法，上面我们讲到Retrofit在决定使用什么callAdapter的时候是看我们在接口中定义的方法的返回值的，而在我们的例子中使用的是<code>RxJava2CallAdapter</code>，因此我们就直接看该类中的adapt方法吧！<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span> </span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">adapt</span><span class="params">(Call&lt;R&gt; call)</span> </span>&#123;</span><br><span class="line">    Observable&lt;Response&lt;R&gt;&gt; responseObservable = isAsync</span><br><span class="line">        ? <span class="keyword">new</span> CallEnqueueObservable&lt;&gt;(call)</span><br><span class="line">        : <span class="keyword">new</span> CallExecuteObservable&lt;&gt;(call);</span><br><span class="line"></span><br><span class="line">    Observable&lt;?&gt; observable;</span><br><span class="line">    <span class="keyword">if</span> (isResult) &#123;</span><br><span class="line">      observable = <span class="keyword">new</span> ResultObservable&lt;&gt;(responseObservable);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isBody) &#123;</span><br><span class="line">      observable = <span class="keyword">new</span> BodyObservable&lt;&gt;(responseObservable);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      observable = responseObservable;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (scheduler != <span class="keyword">null</span>) &#123;</span><br><span class="line">      observable = observable.subscribeOn(scheduler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (isFlowable) &#123;</span><br><span class="line">      <span class="keyword">return</span> observable.toFlowable(BackpressureStrategy.LATEST);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isSingle) &#123;</span><br><span class="line">      <span class="keyword">return</span> observable.singleOrError();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isMaybe) &#123;</span><br><span class="line">      <span class="keyword">return</span> observable.singleElement();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isCompletable) &#123;</span><br><span class="line">      <span class="keyword">return</span> observable.ignoreElements();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> observable;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p><p>首先在adapt方法中会先判断是同步请求还是异步请求，这里我们以同步请求为例，直接看CallExecuteObservable。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">CallExecuteObservable</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">Observable</span>&lt;<span class="title">Response</span>&lt;<span class="title">T</span>&gt;&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Call&lt;T&gt; originalCall;</span><br><span class="line"></span><br><span class="line">  CallExecuteObservable(Call&lt;T&gt; originalCall) &#123;</span><br><span class="line">    <span class="keyword">this</span>.originalCall = originalCall;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">subscribeActual</span><span class="params">(Observer&lt;? <span class="keyword">super</span> Response&lt;T&gt;&gt; observer)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Since Call is a one-shot type, clone it for each new observer.</span></span><br><span class="line">    Call&lt;T&gt; call = originalCall.clone();</span><br><span class="line">    observer.onSubscribe(<span class="keyword">new</span> CallDisposable(call));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">boolean</span> terminated = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">//重点看这里</span></span><br><span class="line">      Response&lt;T&gt; response = call.execute();</span><br><span class="line">      <span class="keyword">if</span> (!call.isCanceled()) &#123;</span><br><span class="line">        observer.onNext(response);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!call.isCanceled()) &#123;</span><br><span class="line">        terminated = <span class="keyword">true</span>;</span><br><span class="line">        observer.onComplete();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">      Exceptions.throwIfFatal(t);</span><br><span class="line">      <span class="keyword">if</span> (terminated) &#123;</span><br><span class="line">        RxJavaPlugins.onError(t);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!call.isCanceled()) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          observer.onError(t);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable inner) &#123;</span><br><span class="line">          Exceptions.throwIfFatal(inner);</span><br><span class="line">          RxJavaPlugins.onError(<span class="keyword">new</span> CompositeException(t, inner));</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">CallDisposable</span> <span class="keyword">implements</span> <span class="title">Disposable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Call&lt;?&gt; call;</span><br><span class="line"></span><br><span class="line">    CallDisposable(Call&lt;?&gt; call) &#123;</span><br><span class="line">      <span class="keyword">this</span>.call = call;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">dispose</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      call.cancel();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isDisposed</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> call.isCanceled();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在subscribeActual方法中去调用了OKHttpCall的execute方法开始进行网络请求，网络请求完毕之后，会通过RxJava的操作符对返回来的数据进行转换，并进行线程的切换，至此，Retrofit的一次使用也就结束了。最后我们再用一张完整的流程图总结上述的几个过程。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-9-04Retrofit/create2.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>相信通过上面的详解，大家对Retrofit应该有了一个比较全面的认识，与其说它是一个网络请求框架不如说他做了一层封装，使得我们能够更方便的间接使用了RxJava与OkHttp。从某种意义上来讲我们从源码中更应该学习其对设计模式的正确运用，使得整个框架的耦合度大大降低，调用者也使用得更加简洁。最后希望这篇文章能够对大家的面试有所帮助！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天是九月的第四天了，学校也正式开学，趁着大学最后一年的这大好时光，抓紧时间赶快学习新知识吧！今天想要与大家一起分享的是Retrofit，由于网上已经有许多讲解Retrofit使用的文章了，本篇文章只会给一个小小的示例，以这个示例作为入口分析其源码，同样也会贴上流程图，以免迷路。话不多说，我们开始吧！！！&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Retrofit" scheme="http://www.wensibo.top/tags/Retrofit/"/>
    
  </entry>
  
  <entry>
    <title>实习总结</title>
    <link href="http://www.wensibo.top/2017/08/31/trainee/"/>
    <id>http://www.wensibo.top/2017/08/31/trainee/</id>
    <published>2017-08-31T02:20:20.000Z</published>
    <updated>2018-10-21T14:53:47.696Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>距离上篇文章已经过去一个多月了，这段时间之所以没有更新文章不是因为偷懒，而是因为在实习。7月份的时候来到了目前的这家公司实习，当初笔试的时候自己做的不是很好，后来面试时也有些地方变现地也不尽如人意，不过最后还是很感激我老大给我来公司实习的机会。在实习的一个多月时间内自己也学到了很多，今天这篇文章就记录一下我的学习过程。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-8-31trainee/trainee.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><a id="more"></a>        <div id="aplayer-ezIUjowE" class="aplayer aplayer-tag-marker" style="margin-bottom: 20px;">            <pre class="aplayer-lrc-content"></pre>        </div>        <script>          var ap = new APlayer({            element: document.getElementById("aplayer-ezIUjowE"),            narrow: false,            autoplay: true,            showlrc: false,            music: {              title: "告白气球",              author: "周杰伦",              url: "http://wensibo.top/file/img/2017-8-31trainee/balloon.mp3",              pic: "http://wensibo.top/file/img/2017-8-31trainee/balloon.jpg",              lrc: ""            }          });          window.aplayers || (window.aplayers = []);          window.aplayers.push(ap);        </script><h1 id="学校-amp-公司的区别"><a href="#学校-amp-公司的区别" class="headerlink" title="学校&amp;公司的区别"></a>学校&amp;公司的区别</h1><p>来到公司实习，其实自己已然不是一个学生了，别人也不会当你是学生，所以很多事情上需要自己去跟上团队的节奏。学校里学到的东西不能说没用，但是与实际的公司其实是有许多不匹配的，我们更需要将自己学到的知识运用在实践中，而不是单纯地纸上谈兵；从另外一个角度上来讲这也是为什么企业招员工时很喜欢招那些有一定的项目经验的学生。很幸运的是自己之前有过一定的项目经验，虽然谈不上是多大的项目，但是这些经历足以培养一个人独立完成工作、独立解决问题的能力，这点也恰恰是在课本上很难学到，但是在实践中却又很有必要积累的。<br>上面的道理大家都懂，但是没什么卵用，我举个例子向大家说明一下这个问题。<br><strong>我有一个工作内容是阅读之前的一个eclipse工程，并将这个工程移植到Android Studio平台上</strong><br>大家或许觉得这个工作内容很简单啊，Android Studio本身就很强大，完全可以解决这个问题。实不相瞒，我一开始也是这样想的，但是当我阅读这个旧的工程的时候，我觉得自己回到了”远古时代”，之所以会有这样的感叹，不是因为代码写的不好，而是整个工程缺乏一定的架构思想，导致一个Activity文件动不动就600~700行，有的甚至到了1000行，尽管逻辑不复杂，但是性能肯定是大打折扣的，并且如果工程日后是别人接手，或者日后需要扩展功能，那么将会彻底地违背了开闭原则（对扩展开放，对修改关闭）。也是基于这样的理由，我就打算将整个项目进行重构，而重构使用的方法则是我经常在项目中使用的MVP设计架构，尽管这种架构仍然有他诟病的地方（代码量不少反增，逻辑也会更加的复杂），但是这仍然不失为一个较好的选择。确定了目标，我也就开始干了，也正因为有了之前项目的积累，所以重构起来也才得心应手。</p><h1 id="需要慢慢培养的技能和规范"><a href="#需要慢慢培养的技能和规范" class="headerlink" title="需要慢慢培养的技能和规范"></a>需要慢慢培养的技能和规范</h1><h2 id="技能"><a href="#技能" class="headerlink" title="技能"></a>技能</h2><p>做程序开发，经验是需要慢慢积累的，而技能也不是一下子就炉火纯青的，需要经历项目的考验才能慢慢成为巨人，在这里我列举一下个人觉得比较重要的开发技能。</p><h3 id="文档阅读能力"><a href="#文档阅读能力" class="headerlink" title="文档阅读能力"></a>文档阅读能力</h3><p>许多大公司都有维护文档的习惯，并且文档的数量和质量也都是顶呱呱的，作为进入团队的新人，对于业务不熟悉的时候，第一时间并不是问老大问同事，而应该自己阅读文档，当然不得不承认的一点就是我一开始是比较笨的，遇到问题就问我老大问我同事，到了后来我才悟到这点，也算是积累吧！</p><p>前面讲的是要有阅读文档的习惯，接下来讲讲要怎么去阅读文档。想必大家或多或少都会看Android官方的文档吧，但是应该不是每个人都看得下的，这里我也承认其实我对官方文档还是有些许排斥的，不仅仅是有的时候都是英文，增加了阅读的难度，当然对于本科生而言，英语阅读不应该成为开发的阻碍，再者就是尽管将英文翻译成了中文，读起来还是有些许的晦涩拗口（也许是我个人的感受），但是。。。不得不承认的就是官方文档是最权威的，并且它的很多内容是很有帮助的，毕竟文档是由项目的开发者编写的，没有人比开发者还懂项目了吧！另外文档中有的时候还会记录一些开发者遇到的坑，作为项目的接手者，如何避免跳入这些坑，看这些文档就对了。我个人的建议就是：</p><ul><li><strong>要静下心来阅读，并且适当的做一下阅读的笔记，将冗杂的内容提炼出真正对自己有用的东西，这里推荐一个Chrome的插件——<a href="http://ksria.com/simpread/" target="_blank" rel="noopener">简悦</a>，他能让你沉浸在阅读之中，排除掉页面其他无关元素的干扰</strong><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://ojec5ddd5.bkt.clouddn.com/read%20mode.png" alt="简悦图示" title="">                </div>                <div class="image-caption">简悦图示</div>            </figure></li><li><strong>再者就是不要妄想一下子就读完整个文档，毕竟这是很多开发者花了许久才编写完成的，我们要做的就是阅读与你相关的内容，或者你感兴趣的内容，这样的效率才会比较高一点。</strong></li></ul><h3 id="独立解决问题的能力"><a href="#独立解决问题的能力" class="headerlink" title="独立解决问题的能力"></a>独立解决问题的能力</h3><p>文章开头讲到我们在课本上学到的知识很多时候并不会派上用场，但是当真正需要的时候我们却早已遗忘，如果你在开发的过程中遇到了一些困难，首先并不应该也不推荐直接向自己的同事询问解决方式，毕竟别人也有工作要做，这里我非常感谢我的同事和老大，因为刚进公司时初出茅庐，很多事情都不是很懂，向他们请教了好多好多，但是大家都十分的nice，很耐心的为我解答，他们帮助我很快的熟悉了业务，非常感谢他们。<br>话说回来如何独立的解决问题呢？以下列举一些我积累的方法，不过大家平常都有用到的啦！</p><ul><li><p><strong>善于利用搜索引擎尤其是Google。搜索引擎装的东西肯定是要比人脑多的，并且互联网为全世界的网民提供了知识分享的平台，你遇到的这个问题或许别人也遇到过，并且已经有了解决方案。</strong></p></li><li><p><strong>利用好<a href="https://stackoverflow.com/" target="_blank" rel="noopener">Stack Overflow</a> 。这是一个编程问题问答平台，很多人遇到问题之后都会来这里提问，如果你对某些问题有了解决方法，那么就慷慨的给出你的答案吧！</strong></p></li><li><p><strong>仔细分析代码。如果上述两个方法都不能解决你的问题，那接下来就得靠你自己了，有可能是你写的代码存在某些问题，这个需要你耐心地去排查，如果问题解决了，那么你应该在你的文档或者笔记中记录下这个问题，为团队提供解决方案，而对自己而言也是一种积累。</strong></p></li></ul><h2 id="规范"><a href="#规范" class="headerlink" title="规范"></a>规范</h2><p>规范在企业中十分地重要，体现在软件开发中就是指代码的编写规范、工具的使用规范、版本控制工具的使用规范、文档的编写规范等等。这里讲讲<strong>代码的规范和版本控制工具的使用规范</strong>。<br>其实两者的关系十分的密切，因为很多时候代码是需要提交到版本控制系统上的，在这里我就指公司使用的比较多的SVN了。举一个例子，也是我老大跟我们强调的一点，在开发过程中代码的每行的缩距虽然并不是特别的重要，很多时候每个人都有每个人不同的缩距方式，但是这在团队协同工作的时候就会存在问题，例如我将Android Studio的默认行缩距进行了调整，将代码提交到了SVN，接下来我的另外一个同事查看我的代码时，发现缩距有点奇怪，于是为了阅读的方便，他将缩距调整为自己能够接受的程度，当阅读完代码之后，SVN提示我的同事已经将代码修改了，但是实际上他并没有对代码做一些实质性的修改，只是做了缩距的修改，但这仍然被SVN识别成一次成功的提交，所以这就是问题所在了。解决问题的方法就是团队约定一个准则，使用IDE的默认缩距设置，这样就不会存在这种问题啦！</p><h1 id="接触和学习新知识"><a href="#接触和学习新知识" class="headerlink" title="接触和学习新知识"></a>接触和学习新知识</h1><p>正所谓术业有专攻，每个人都有自己擅长的方面，但是知识是不断更新的，并且也很少人能够做到对整个知识体系的每一块都了然于胸，所以如果到了新的团队，接手新的业务，而开发内容是你不熟悉的，那也没有必要慌张，这个时候你得尽快的熟悉这方面的知识，通过许多的手段去让自己融入团队，这个才是新手的最佳技能。</p><h1 id="尾声"><a href="#尾声" class="headerlink" title="尾声"></a>尾声</h1><p>以上就是这段实习经历中我学到的一些经验，写出来与大家一起分享，也当做是这段实习经历的总结，对以后的工作或许会有帮助。希望大家会喜欢！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;距离上篇文章已经过去一个多月了，这段时间之所以没有更新文章不是因为偷懒，而是因为在实习。7月份的时候来到了目前的这家公司实习，当初笔试的时候自己做的不是很好，后来面试时也有些地方变现地也不尽如人意，不过最后还是很感激我老大给我来公司实习的机会。在实习的一个多月时间内自己也学到了很多，今天这篇文章就记录一下我的学习过程。&lt;br&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2017-8-31trainee/trainee.jpg&quot; alt=&quot;&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;&lt;/div&gt;
            &lt;/figure&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="工作" scheme="http://www.wensibo.top/categories/%E5%B7%A5%E4%BD%9C/"/>
    
    
      <category term="工作" scheme="http://www.wensibo.top/tags/%E5%B7%A5%E4%BD%9C/"/>
    
  </entry>
  
  <entry>
    <title>从源码的角度看Service是如何启动的</title>
    <link href="http://www.wensibo.top/2017/07/16/service/"/>
    <id>http://www.wensibo.top/2017/07/16/service/</id>
    <published>2017-07-16T06:25:25.000Z</published>
    <updated>2018-10-21T08:26:20.884Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>七月中旬了，大家的实习有着落了吗？秋招又准备的怎么样了呢？我依旧在准备着秋招，每当想到自己以应届生的身份找着工作而工作却不一定要你的时候，难免也会有点失落。互联网行业的大佬们求贤若渴但对贤才也十分的苛刻，看到内推正如火如荼的进行着，深怕自己被这场浪潮甩在身后，所以也不得不苦心的准备着。如果你也是2018届应届生，如果你也看到了这篇文章，请你在留言区留下你找工作，准备秋招的感受，我们一起交流交流。<br>今天接着<a href="http://wensibo.top/2017/07/05/Activity/" target="_blank" rel="noopener">上篇文章</a>一起来看看四大组件的老二——Service。话不多说我们开始吧！</p><a id="more"></a> <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>我们一般使用Service有两种方式，startService和bindService，这两种方法使用场景各有不同，本篇文章以startService为例讲解Service的启动过程，而bindService大体上与startService相近，只是一些逻辑调用上有所区别。<br>在这里我先贴上通过本次分析得到的Service完整的启动流程图，现在不需要理解其中的过程，只需要一步步分析源码的时候回过头来看看这幅图，以免迷失方向。当然我在每一步都会贴出相对应的流程图。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/process3.png" alt="总体流程图" title="">                </div>                <div class="image-caption">总体流程图</div>            </figure></p><h2 id="认识ContextImpl"><a href="#认识ContextImpl" class="headerlink" title="认识ContextImpl"></a>认识ContextImpl</h2><p>首先先给出一张类图，我们从大局上看一下这些类的关系。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/1.png" alt="类图" title="">                </div>                <div class="image-caption">类图</div>            </figure><br>从上面这张图我们可以看到Activity继承了ContextWrapper类，而在ContextWrapper类中，实现了startService方法。在ContextWrapper类中，有一个成员变量mBase，它是一个ContextImpl实例，而ContextImpl类和ContextWrapper类一样继承于Context类。为什么会给出这张图呢？这对我们接下来的分析十分有用。</p><h2 id="启动Service的入口"><a href="#启动Service的入口" class="headerlink" title="启动Service的入口"></a>启动Service的入口</h2><p>我们在Activity中使用<code>startService(Intent service)</code>来启动一个服务，其调用的方法如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ContextWrapper类</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ComponentName <span class="title">startService</span><span class="params">(Intent service)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> mBase.startService(service);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到其调用了ContextWrapper的startService方法，而这个方法内部使用mBase的startService方法，我们再看回刚才的类图，可以看到，这里的mBase其实就是ContextImpl，从而我们可以得出ContextWrapper类的startService方法最终是通过调用ContextImpl类的startService方法来实现的。那接下来就来看看ContextImpl.startService()。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ComponentName <span class="title">startService</span><span class="params">(Intent service)</span> </span>&#123;</span><br><span class="line">    warnIfCallingFromSystemProcess();</span><br><span class="line">    <span class="keyword">return</span> startServiceCommon(service, mUser);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> ComponentName <span class="title">startServiceCommon</span><span class="params">(Intent service, UserHandle user)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        validateServiceIntent(service);</span><br><span class="line">        service.prepareToLeaveProcess(<span class="keyword">this</span>);</span><br><span class="line">        ComponentName cn = ActivityManagerNative.getDefault().startService(</span><br><span class="line">            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(</span><br><span class="line">                        getContentResolver()), getOpPackageName(), user.getIdentifier());</span><br><span class="line">        <span class="keyword">if</span> (cn != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (cn.getPackageName().equals(<span class="string">"!"</span>)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(</span><br><span class="line">                        <span class="string">"Not allowed to start service "</span> + service</span><br><span class="line">                        + <span class="string">" without permission "</span> + cn.getClassName());</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (cn.getPackageName().equals(<span class="string">"!!"</span>)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(</span><br><span class="line">                        <span class="string">"Unable to start service "</span> + service</span><br><span class="line">                        + <span class="string">": "</span> + cn.getClassName());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cn;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> e.rethrowFromSystemServer();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们可以看到startService方法其实是调用了startServiceCommon，而startServiceCommon做了些什么呢？我们看到了一个熟悉的家伙：<code>ActivityManagerNative.getDefault()</code>，在<a href="http://wensibo.top/2017/07/05/Activity/" target="_blank" rel="noopener">上篇文章</a>分析Activity启动的时候我们也看过他，并且我们也知道ActivityManagerNative.getDefault()返回的就是一个ActivityManagerProxy对象，这里使用Binder机制（如果你对Binder机制不是很了解，那可以看一下我之前写的<a href="http://wensibo.top/2017/07/03/Binder/" target="_blank" rel="noopener">这篇文章</a>）将代理Proxy返回给客户端，而客户端通过将参数写入Proxy类，接着Proxy就会通过Binder去远程调用服务端的具体方法，因此，我们只是借用ActivityManagerProxy来调用ActivityManagerService的方法。所以这里其实是调用了远程ActivityManagerService的startService方法。<br>到这里我们先用流程图来看一下目前Service启动的情况<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/process1.png" alt="流程图1" title="">                </div>                <div class="image-caption">流程图1</div>            </figure><br>接下来我们就看看ActivityManagerService是如何实现的。</p><h2 id="AMS如何进一步启动Service"><a href="#AMS如何进一步启动Service" class="headerlink" title="AMS如何进一步启动Service"></a>AMS如何进一步启动Service</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityManagerService类</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ComponentName <span class="title">startService</span><span class="params">(IApplicationThread caller, Intent service,</span></span></span><br><span class="line"><span class="function"><span class="params">        String resolvedType, <span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">synchronized</span>(<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> callingPid = Binder.getCallingPid();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> callingUid = Binder.getCallingUid();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">long</span> origId = Binder.clearCallingIdentity();</span><br><span class="line">        ComponentName res = mServices.startServiceLocked(caller, service,</span><br><span class="line">                resolvedType, callingPid, callingUid, userId);</span><br><span class="line">        Binder.restoreCallingIdentity(origId);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到这里调用了mService的startServiceLocked方法，那mService是干嘛的呢？此处的mServices是一个ActiveServices对象，从名字上也能看出该类主要是封装了一些处于活动状态的service组件的方法的调用。那接下来就看看他的startServiceLocked是如何实现的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ComponentName <span class="title">startServiceLocked</span><span class="params">(IApplicationThread caller,</span></span></span><br><span class="line"><span class="function"><span class="params">        Intent service, String resolvedType,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">int</span> callingPid, <span class="keyword">int</span> callingUid, <span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">boolean</span> callerFg;</span><br><span class="line">    <span class="keyword">if</span> (caller != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//mAm 是ActivityManagerService.</span></span><br><span class="line">        <span class="keyword">final</span> ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);</span><br><span class="line">        <span class="keyword">if</span> (callerApp == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(</span><br><span class="line">                    <span class="string">"Unable to find app for caller "</span> + caller</span><br><span class="line">                    + <span class="string">" (pid="</span> + Binder.getCallingPid()</span><br><span class="line">                    + <span class="string">") when starting service "</span> + service);</span><br><span class="line">        &#125;</span><br><span class="line">        callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        callerFg = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, </span><br><span class="line">                     callingPid, callingUid, userId, <span class="keyword">true</span>, callerFg);</span><br><span class="line">    <span class="keyword">if</span> (res == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (res.record == <span class="keyword">null</span>) &#123;<span class="comment">//权限拒绝.</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ComponentName(<span class="string">"!"</span>, res.permission != <span class="keyword">null</span></span><br><span class="line">                ? res.permission : <span class="string">"private to package"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ServiceRecord r = res.record;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    r.lastActivity = SystemClock.uptimeMillis();</span><br><span class="line">    r.startRequested = <span class="keyword">true</span>;</span><br><span class="line">    r.delayedStop = <span class="keyword">false</span>;</span><br><span class="line">    r.pendingStarts.add(<span class="keyword">new</span> ServiceRecord.StartItem(r, <span class="keyword">false</span>, r.makeNextStartId(),</span><br><span class="line">            service, neededGrants));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> ServiceMap smap = getServiceMap(r.userId);</span><br><span class="line">    <span class="keyword">boolean</span> addToStarting = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">return</span> startServiceInnerLocked(smap, service, r, callerFg, addToStarting);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>代码稍微有点长，但是最重要的逻辑在于最后一句的startServiceInnerLocked方法，他的内部实现是这样的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ComponentName <span class="title">startServiceInnerLocked</span><span class="params">(ServiceMap smap, Intent service,</span></span></span><br><span class="line"><span class="function"><span class="params">        ServiceRecord r, <span class="keyword">boolean</span> callerFg, <span class="keyword">boolean</span> addToStarting)</span> </span>&#123; </span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//真正开启service的地方。</span></span><br><span class="line">    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, <span class="keyword">false</span>);</span><br><span class="line">    <span class="comment">//注意此处反回值是null的时候，证明没有异常.</span></span><br><span class="line">    <span class="keyword">if</span> (error != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ComponentName(<span class="string">"!!"</span>, error);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">return</span> r.name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>startServiceInnerLocked方法内部调用了bringUpServiceLocked方法来进行后续的启动。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> String <span class="title">bringUpServiceLocked</span><span class="params">(ServiceRecord r, <span class="keyword">int</span> intentFlags, <span class="keyword">boolean</span> execInFg,</span></span></span><br><span class="line"><span class="function"><span class="params">    <span class="keyword">boolean</span> whileRestarting)</span> <span class="keyword">throws</span> TransactionTooLargeException </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">if</span> (app != <span class="keyword">null</span> &amp;&amp; app.thread != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);</span><br><span class="line">        realStartServiceLocked(r, app, execInFg);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">      &#125; </span><br><span class="line"></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在bringUpServiceLocked方法中调用了realStartServiceLocked方法，在Activity的启动过程中我们也曾看过相似的方法，说明到了这里我们也快看到真正的Service启动了，接着来看realStartServiceLocked。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">realStartServiceLocked</span><span class="params">(ServiceRecord r,</span></span></span><br><span class="line"><span class="function"><span class="params">    ProcessRecord app, <span class="keyword">boolean</span> execInFg)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//...</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">boolean</span> created = <span class="keyword">false</span>;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);</span><br><span class="line">    app.thread.scheduleCreateService(r, r.serviceInfo,</span><br><span class="line">        mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),</span><br><span class="line">        app.repProcState);</span><br><span class="line">    r.postNotification();</span><br><span class="line">    created = <span class="keyword">true</span>;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (DeadObjectException e) &#123;</span><br><span class="line">    Slog.w(TAG, <span class="string">"Application dead when creating service "</span> + r);</span><br><span class="line">    mAm.appDiedLocked(app);</span><br><span class="line">    <span class="keyword">throw</span> e;</span><br><span class="line">  &#125; </span><br><span class="line"></span><br><span class="line">  <span class="comment">//这里会调用Service的onStartCommand</span></span><br><span class="line">  sendServiceArgsLocked(r, execInFg, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//...</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们先用流程图看看Service启动的逻辑。接着在下面会着重讲解上方的realStartServiceLocked方法。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/process2.png" alt="流程图2" title="">                </div>                <div class="image-caption">流程图2</div>            </figure></p><h2 id="realStartServiceLocked"><a href="#realStartServiceLocked" class="headerlink" title="realStartServiceLocked"></a>realStartServiceLocked</h2><p>这里需要对上面的<code>app.thread</code>做一下特殊的说明。如果你已经了解了Binder的通信机制，那你应该知道一般我们的服务都是由客户端向服务端发出请求，接着服务端向客户端返回结果，这个是单向的通信，但是如果反过来服务端要向客户端发送请求的话，那么同样的，在服务端也应该持有另外一个Proxy，而在客户端也同样需要一个Manager与之对应。<br>在这里<code>app</code>是要运行 Service 的进程对应的ProcessRecord对象，代表一个应用进程，而<code>thread</code>是一个ApplicationThreadProxy对象，它运行在AMS(现在AMS就是客户端了)，而与之对应的服务端则是在应用程序中的ApplicatonThread，还是有点绕，我们用一张图来展示他们的关系。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/server2client.png" alt="两对Binder" title="">                </div>                <div class="image-caption">两对Binder</div>            </figure><br>相信通过上面的图示大家应该能够明白应用程序进程与系统服务进程之间的双向通信了吧！</p><p>还需要再唠叨一下ApplicationThread与ApplicationThreadProxy之间的关系，我们通过这两个类的定义可以更深刻的理解他们的关系</p><ul><li><strong>IApplicationThread</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IApplicationThread</span> <span class="keyword">extends</span> <span class="title">IInterface</span></span></span><br></pre></td></tr></table></figure><p><code>IApplicationThread</code>其实是一个IBinder类型的接口。并且在这个接口中声明了许多与Activity，Service生命周期相关的方法，那么它的实现类又是谁呢？答案就是<code>ApplicationThreadNative</code>。</p><ul><li><strong>ApplicationThreadNative</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ApplicationThreadNative</span> <span class="keyword">extends</span> <span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">IApplicationThread</span></span></span><br></pre></td></tr></table></figure><p>可以看到ApplicationThreadNative是一个抽象类，我们不能直接创建其对象，应该使用其子类，而恰好<code>ApplicationThread</code>就是其子类</p><ul><li><strong>ApplicationThread</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ApplicationThread</span> <span class="keyword">extends</span> <span class="title">ApplicationThreadNative</span></span></span><br></pre></td></tr></table></figure><p>ApplicationThread就是真正意义上的服务端，它的父类<code>ApplicationThreadNative</code>就是将具体的操作将给它来执行的。</p><ul><li><strong>ApplicationThreadProxy</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ApplicationThreadProxy</span> <span class="keyword">implements</span> <span class="title">IApplicationThread</span></span></span><br></pre></td></tr></table></figure><p>说了那么多，在客户端运行的ApplicationThreadProxy在哪里呢？其实如果理解了Binder机制，那么我们应该知道他就是ApplicationThreadNative的内部类，客户端(AMS)就是通过它与service所在的进程进行通信的。因此我们接着要看的当然是ApplicationThread的scheduleCreateService了。</p><h2 id="ApplicationThread-scheduleCreateService"><a href="#ApplicationThread-scheduleCreateService" class="headerlink" title="ApplicationThread.scheduleCreateService"></a>ApplicationThread.scheduleCreateService</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">scheduleCreateService</span><span class="params">(IBinder token,</span></span></span><br><span class="line"><span class="function"><span class="params">                ServiceInfo info, CompatibilityInfo compatInfo, <span class="keyword">int</span> processState)</span> </span>&#123;</span><br><span class="line">            updateProcessState(processState, <span class="keyword">false</span>);</span><br><span class="line">            CreateServiceData s = <span class="keyword">new</span> CreateServiceData();</span><br><span class="line">            s.token = token;</span><br><span class="line">            s.info = info;</span><br><span class="line">            s.compatInfo = compatInfo;</span><br><span class="line"></span><br><span class="line">            sendMessage(H.CREATE_SERVICE, s);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>可以看到在scheduleCreateService方法中发送了一个message，其消息类型为<code>CREATE_SERVICE</code>，它是在H类中定义的一个常量，而H类其实就是继承于Handler的，它专门用来处理发送过来的请求。接下来就来看看它是如何处理创建service这个消息的。</p><h2 id="H-handleMessage"><a href="#H-handleMessage" class="headerlink" title="H.handleMessage"></a>H.handleMessage</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">case</span> CREATE_SERVICE:</span><br><span class="line">            handleCreateService((CreateServiceData)msg.obj); <span class="comment">//【见流程15】</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> BIND_SERVICE:</span><br><span class="line">            handleBindService((BindServiceData)msg.obj);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> UNBIND_SERVICE:</span><br><span class="line">            handleUnbindService((BindServiceData)msg.obj);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到handleMessage处理了很多类型的消息，包括service的创建、绑定、解绑、销毁等等，我们直接看创建的逻辑，也就是handleCreateService方法。不过有一个问题，那就是Handler创建之前必须要创建Looper，否则会报错，那么Looper是在哪里创建的呢？答案就是ActivityThread的main方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        <span class="comment">//在主线程创建Looper</span></span><br><span class="line">        Looper.prepareMainLooper();</span><br><span class="line"></span><br><span class="line">        ActivityThread thread = <span class="keyword">new</span> ActivityThread();</span><br><span class="line">        thread.attach(<span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sMainThreadHandler == <span class="keyword">null</span>) &#123;</span><br><span class="line">            sMainThreadHandler = thread.getHandler();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        AsyncTask.init();</span><br><span class="line">        Looper.loop();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Main thread loop unexpectedly exited"</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>我们可以看到Looper就是在这里创建的，而Handler也是在Service进程中的主线程，也就是说它处理消息也是在主线程，那么Service的创建自然也就是在主线程中。可是ActivityThread是什么鬼呢？其实刚才的ApplicationThread就是它的内部类。<br>接下来继续看Handler如何处理消息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleCreateService</span><span class="params">(CreateServiceData data)</span> </span>&#123;</span><br><span class="line">    unscheduleGcIdler();</span><br><span class="line">    LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);</span><br><span class="line"></span><br><span class="line">    java.lang.ClassLoader cl = packageInfo.getClassLoader();</span><br><span class="line">    <span class="comment">//通过反射创建目标服务对象</span></span><br><span class="line">    Service service = (Service) cl.loadClass(data.info.name).newInstance();</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">//创建ContextImpl对象</span></span><br><span class="line">        ContextImpl context = ContextImpl.createAppContext(<span class="keyword">this</span>, packageInfo);</span><br><span class="line">        context.setOuterContext(service);</span><br><span class="line">        <span class="comment">//创建Application对象</span></span><br><span class="line">        Application app = packageInfo.makeApplication(<span class="keyword">false</span>, mInstrumentation);</span><br><span class="line">        service.attach(context, <span class="keyword">this</span>, data.info.name, data.token, app,</span><br><span class="line">                ActivityManagerNative.getDefault());</span><br><span class="line">        <span class="comment">//调用服务onCreate()方法</span></span><br><span class="line">        service.onCreate();</span><br><span class="line">        mServices.put(data.token, service);</span><br><span class="line">        <span class="comment">//调用服务创建完成</span></span><br><span class="line">        ActivityManagerNative.getDefault().serviceDoneExecuting(</span><br><span class="line">                data.token, SERVICE_DONE_EXECUTING_ANON, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这里Handler通过反射机制拿到Service对象，于是就调用了service的onCreate方法，所以Service就算是启动了。我们通过层层的寻找总算是见到了onCreate的庐山真面目。<br>我们依旧用流程图来展示一下Service启动的全过程。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-15Service/process3.png" alt="流程图3" title="">                </div>                <div class="image-caption">流程图3</div>            </figure></p><h2 id="onStartCommand"><a href="#onStartCommand" class="headerlink" title="onStartCommand"></a>onStartCommand</h2><p>可是还有另外一个重要的方法<code>onStartCommand</code>呢？不急，我们刚才在<code>sendServiceArgsLocked</code>方法中还有另外一个<code>sendServiceArgsLocked</code>方法没有讲到，他就是onStartCommand的入口，我们看看他是如何实现的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">sendServiceArgsLocked</span><span class="params">(ServiceRecord r, <span class="keyword">boolean</span> execInFg,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> oomAdjusted)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> N = r.pendingStarts.size();</span><br><span class="line">        <span class="keyword">if</span> (N == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (r.pendingStarts.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//与调用scheduleCreateService方法一样，远程调用ApplicationThread的scheduleServiceArgs方法</span></span><br><span class="line">                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                <span class="comment">// Remote process gone...  we'll let the normal cleanup take</span></span><br><span class="line">                <span class="comment">// care of this.</span></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG, <span class="string">"Crashed while scheduling start: "</span> + r);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                Slog.w(TAG, <span class="string">"Unexpected exception"</span>, e);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>我们看到这里依旧调用了远程服务端ApplicationThread的方法来执行后面的逻辑，其实通过上面分析onCreate的逻辑大家应该能够知道调用onStartCommand也是大同小异的，具体的调用逻辑就交给大家去分析啦！</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>到这里呢，我们就把Service的启动流程完整的讲解了一遍，希望大家通过这篇文章能够对Service更加的了解，毕竟四大组件中Service的地位还是举足轻重的，并且在面试中也会经常被问到，希望大家逢问必会O(∩_∩)O哈哈~</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;七月中旬了，大家的实习有着落了吗？秋招又准备的怎么样了呢？我依旧在准备着秋招，每当想到自己以应届生的身份找着工作而工作却不一定要你的时候，难免也会有点失落。互联网行业的大佬们求贤若渴但对贤才也十分的苛刻，看到内推正如火如荼的进行着，深怕自己被这场浪潮甩在身后，所以也不得不苦心的准备着。如果你也是2018届应届生，如果你也看到了这篇文章，请你在留言区留下你找工作，准备秋招的感受，我们一起交流交流。&lt;br&gt;今天接着&lt;a href=&quot;http://wensibo.top/2017/07/05/Activity/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;上篇文章&lt;/a&gt;一起来看看四大组件的老二——Service。话不多说我们开始吧！&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Android源码解析" scheme="http://www.wensibo.top/tags/Android%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
    
      <category term="Service" scheme="http://www.wensibo.top/tags/Service/"/>
    
  </entry>
  
  <entry>
    <title>从源码的角度看Activity是如何启动的</title>
    <link href="http://www.wensibo.top/2017/07/05/Activity/"/>
    <id>http://www.wensibo.top/2017/07/05/Activity/</id>
    <published>2017-07-05T05:24:25.000Z</published>
    <updated>2018-10-21T08:26:20.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>大家好，今天想与大家一起分享的是Activity。我们平时接触的最多的就是Activity了，作为四大组件中最为重要的老大，Activity究竟是如何启动的呢？这篇文章将会从源码的角度为大家进行全方位的解析，为了方便大家理解整个的过程，我会用流程图的方式将整个过程串起来，希望对大家有所帮助。<br><a id="more"></a> </p><h1 id="开始吧！"><a href="#开始吧！" class="headerlink" title="开始吧！"></a>开始吧！</h1><p>一般我们启动Activity有两种方法，这里我就不再详细说这两种方法的用法了，不过他们都是调用了同样的一个逻辑<code>startActivity</code>。所以我们分析Activity的启动流程就从这个方法开始。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivity</span><span class="params">(Intent intent, @Nullable Bundle options)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (options != <span class="keyword">null</span>) &#123;</span><br><span class="line">        startActivityForResult(intent, -<span class="number">1</span>, options);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        startActivityForResult(intent, -<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到尽管startActivity()有多种重载方式，但是最终调用的还是<code>startActivityForResult</code>，所以我们只需要看startActivityForResult里面的实现逻辑即可。<strong>这里需要注意的一点就是调用了startActivityForResult方法时传入的一个参数为<code>-1</code>，为什么是-1呢？还记得我们如果需要下一个Activity返回数据给目前这个Activity的时候都是调用startActivityForResult方法，不会去调用startActivity，因为startActivity尽管最后还是调用startActivityForResult，但是他设置了requestCode参数为-1，二在startActivityForResult方法中会判断requestCode是否大于等于0，如果小于0就不会返回结果，因此我们都会选择startActivityForResult方法以取回结果，并且设置其code参数大于等于0。</strong>下面我们来看看startActivityForResult的实现：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">@RequiresPermission Intent intent, <span class="keyword">int</span> requestCode,@Nullable Bundle options)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//mParent是一个Activity对象，表示该Activity是否由父Activity启动</span></span><br><span class="line">    <span class="keyword">if</span> (mParent == <span class="keyword">null</span>) &#123;</span><br><span class="line">        options = transferSpringboardActivityOptions(options);</span><br><span class="line">        Instrumentation.ActivityResult ar =</span><br><span class="line">            mInstrumentation.execStartActivity(</span><br><span class="line">                <span class="keyword">this</span>, mMainThread.getApplicationThread(), mToken, <span class="keyword">this</span>,</span><br><span class="line">                intent, requestCode, options);</span><br><span class="line">        <span class="keyword">if</span> (ar != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mMainThread.sendActivityResult(</span><br><span class="line">                mToken, mEmbeddedID, requestCode, ar.getResultCode(),</span><br><span class="line">                ar.getResultData());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//这里就是判断requestCode的逻辑</span></span><br><span class="line">        <span class="keyword">if</span> (requestCode &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            mStartedActivity = <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        cancelInputsAndStartExitTransition(options);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (options != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mParent.startActivityFromChild(<span class="keyword">this</span>, intent, requestCode, options);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mParent.startActivityFromChild(<span class="keyword">this</span>, intent, requestCode);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在startActivityForResult方法中，首先会判断该Activity是否由父Activity启动，即mParent，如果他是第一个Activity，就会调用Instrumentation的<code>execStartActivity</code>方法，这里再看一下判断requestCode的逻辑：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (requestCode &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">    mStartedActivity = <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到在这里确实判断了requestCode的大小,大于等于0的时候才会返回结果，否则是不会的。<br>继续说回<code>execStartActivity</code>方法，这里就是正真执行Activity启动的操作，解释一下他的几个参数：</p><ul><li><code>this</code>，为启动Activity的对象</li><li><code>mMainThread.</code>getApplicationThread()，为Binder对象，是主进程的context对象</li><li><code>token</code>，也是一个Binder对象，指向了服务端一个ActivityRecord对象</li><li><code>target</code>，为启动的Activity</li><li><code>intent</code>，启动的Intent对象</li><li><code>requestCode</code>，请求码</li><li><code>options</code>，参数</li></ul><p>这里的第一个Binder对象在我们的整个分析过程中将扮演者非常重要的作用，如果你对Binder不熟悉的话，请到<a href="http://wensibo.top/2017/07/03/Binder/" target="_blank" rel="noopener">这里</a>了解有关Binder机制的内容。<br>接下来是<code>execStartActivity</code>方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ActivityResult <span class="title">execStartActivity</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">        Context who, IBinder contextThread, IBinder token, Activity target,</span></span></span><br><span class="line"><span class="function"><span class="params">        Intent intent, <span class="keyword">int</span> requestCode, Bundle options)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        intent.migrateExtraStreamToClipData();</span><br><span class="line">        intent.prepareToLeaveProcess();</span><br><span class="line">        <span class="keyword">int</span> result = ActivityManagerNative.getDefault()</span><br><span class="line">            .startActivity(whoThread, who.getBasePackageName(), intent,</span><br><span class="line">                    intent.resolveTypeIfNeeded(who.getContentResolver()),</span><br><span class="line">                    token, target != <span class="keyword">null</span> ? target.mEmbeddedID : <span class="keyword">null</span>,</span><br><span class="line">                    requestCode, <span class="number">0</span>, <span class="keyword">null</span>, options);</span><br><span class="line">        checkStartActivityResult(result, intent);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Failure from system"</span>, e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里最重要的就是调用了<code>ActivityManagerNative.getDefault().startActivity()</code>，但是ActivityManagerNative.getDefault()是什么东西呢？我们继续看getDefault()的源码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">public</span> IActivityManager <span class="title">getDefault</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> gDefault.get();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton&lt;IActivityManager&gt; gDefault = <span class="keyword">new</span> Singleton&lt;IActivityManager&gt;() &#123;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> IActivityManager <span class="title">create</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        IBinder b = ServiceManager.getService(<span class="string">"activity"</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">false</span>) &#123;</span><br><span class="line">            Log.v(<span class="string">"ActivityManager"</span>, <span class="string">"default service binder = "</span> + b);</span><br><span class="line">        &#125;</span><br><span class="line">        IActivityManager am = asInterface(b);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">false</span>) &#123;</span><br><span class="line">            Log.v(<span class="string">"ActivityManager"</span>, <span class="string">"default service = "</span> + am);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> am;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p>可以看到其中声明了一个Singleton封装类，其类型是IActivityManager，注意到其中调用了<code>asInterface</code>方法，接着看他做了什么？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">public</span> IActivityManager <span class="title">asInterface</span><span class="params">(IBinder obj)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (obj == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    IActivityManager in =</span><br><span class="line">        (IActivityManager)obj.queryLocalInterface(descriptor);</span><br><span class="line">    <span class="keyword">if</span> (in != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> in;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ActivityManagerProxy(obj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//ActivityManagerProxy：startActivity</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">startActivity</span><span class="params">(IApplicationThread caller, String callingPackage, Intent intent,</span></span></span><br><span class="line"><span class="function"><span class="params">            String resolvedType, IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle options)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">        Parcel data = Parcel.obtain();</span><br><span class="line">        Parcel reply = Parcel.obtain();</span><br><span class="line">        data.writeInterfaceToken(IActivityManager.descriptor);</span><br><span class="line">        data.writeStrongBinder(caller != <span class="keyword">null</span> ? caller.asBinder() : <span class="keyword">null</span>);</span><br><span class="line">        data.writeString(callingPackage);</span><br><span class="line">        intent.writeToParcel(data, <span class="number">0</span>);</span><br><span class="line">        data.writeString(resolvedType);</span><br><span class="line">        data.writeStrongBinder(resultTo);</span><br><span class="line">        data.writeString(resultWho);</span><br><span class="line">        data.writeInt(requestCode);</span><br><span class="line">        data.writeInt(startFlags);</span><br><span class="line">        <span class="keyword">if</span> (profilerInfo != <span class="keyword">null</span>) &#123;</span><br><span class="line">            data.writeInt(<span class="number">1</span>);</span><br><span class="line">            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            data.writeInt(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (options != <span class="keyword">null</span>) &#123;</span><br><span class="line">            data.writeInt(<span class="number">1</span>);</span><br><span class="line">            options.writeToParcel(data, <span class="number">0</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            data.writeInt(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, <span class="number">0</span>);</span><br><span class="line">        reply.readException();</span><br><span class="line">        <span class="keyword">int</span> result = reply.readInt();</span><br><span class="line">        reply.recycle();</span><br><span class="line">        data.recycle();</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>可以看到asInterface返回了一个ActivityManagerProxy对象，也就是说ActivityManagerNative.getDefault()返回的就是一个<code>ActivityManagerProxy</code>对象，通过之前的BInder机制的文章我们可以知道Proxy是运行在客户端的，客户端通过将参数写入Proxy类，接着Proxy就会通过Binder去远程调用服务端的具体方法，因此，我们只是借用ActivityManagerProxy来调用ActivityManagerService的方法，他们之间的关系如下所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-05Activity/AMS_AMP.png" alt="通信方式" title="">                </div>                <div class="image-caption">通信方式</div>            </figure><br>到目前为止Activity的启动流程就是如下所示了，可以看到Activity的启动逻辑来到了AMS中。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-05Activity/activity_1.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><h1 id="在AMS中启动Activity"><a href="#在AMS中启动Activity" class="headerlink" title="在AMS中启动Activity"></a>在AMS中启动Activity</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">startActivity</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span></span><br><span class="line"><span class="function"><span class="params">        Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle options)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,</span><br><span class="line">        resultWho, requestCode, startFlags, profilerInfo, options,</span><br><span class="line">        UserHandle.getCallingUserId());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">startActivityAsUser</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span></span><br><span class="line"><span class="function"><span class="params">        Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle options, <span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">    enforceNotIsolatedCaller(<span class="string">"startActivity"</span>);</span><br><span class="line">    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,</span><br><span class="line">            <span class="keyword">false</span>, ALLOW_FULL_ONLY, <span class="string">"startActivity"</span>, <span class="keyword">null</span>);</span><br><span class="line">    <span class="comment">// <span class="doctag">TODO:</span> Switch to user app stacks here.</span></span><br><span class="line">    <span class="keyword">return</span> mStackSupervisor.startActivityMayWait(caller, -<span class="number">1</span>, callingPackage, intent,</span><br><span class="line">            resolvedType, <span class="keyword">null</span>, <span class="keyword">null</span>, resultTo, resultWho, requestCode, startFlags,</span><br><span class="line">            profilerInfo, <span class="keyword">null</span>, <span class="keyword">null</span>, options, <span class="keyword">false</span>, userId, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在startActivity中直接调用了startActivityAsUser方法，而在startActivityAsUser中则是调用mStackSupervisor.startActivityMayWait方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">int</span> <span class="title">startActivityLocked</span><span class="params">(IApplicationThread caller,</span></span></span><br><span class="line"><span class="function"><span class="params">    Intent intent, String resolvedType, ActivityInfo aInfo,</span></span></span><br><span class="line"><span class="function"><span class="params">    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params">    IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params">    <span class="keyword">int</span> callingPid, <span class="keyword">int</span> callingUid, String callingPackage,</span></span></span><br><span class="line"><span class="function"><span class="params">    <span class="keyword">int</span> realCallingPid, <span class="keyword">int</span> realCallingUid, <span class="keyword">int</span> startFlags, Bundle options,</span></span></span><br><span class="line"><span class="function"><span class="params">    <span class="keyword">boolean</span> ignoreTargetSecurity, <span class="keyword">boolean</span> componentSpecified, ActivityRecord[] outActivity,</span></span></span><br><span class="line"><span class="function"><span class="params">    ActivityContainer container, TaskRecord inTask)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> err = ActivityManager.START_SUCCESS;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,</span><br><span class="line">        startFlags, <span class="keyword">true</span>, options, inTask);</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">return</span> err;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>这个方法中主要构造了ActivityManagerService端的Activity对象:ActivityRecord，并根据Activity的启动模式执行了相关逻辑。然后调用了startActivityUncheckedLocked方法，而在startActivityUncheckedLocked中则调用了startActivityUncheckedLocked方法，startActivityUncheckedLocked方法则会调用startActivityLocked方法，startActivityLocked又会调用resumeTopActivitiesLocked方法，其最后调用了resumeTopActivityLocked方法。<br>经过一系列的调用之后，最终来到了startPausingLocked方法，它会执行Activity的onPause方法，从而结束当前的Activity。<br>首先来看startPausingLocked方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">startPausingLocked</span><span class="params">(<span class="keyword">boolean</span> userLeaving, <span class="keyword">boolean</span> uiSleeping, <span class="keyword">boolean</span> resuming,<span class="keyword">boolean</span> dontWait)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> (prev.app != <span class="keyword">null</span> &amp;&amp; prev.app.thread != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (DEBUG_PAUSE) Slog.v(TAG_PAUSE, <span class="string">"Enqueueing pending pause: "</span> + prev);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,</span><br><span class="line">                    prev.userId, System.identityHashCode(prev),</span><br><span class="line">                    prev.shortComponentName);</span><br><span class="line">            mService.updateUsageStats(prev, <span class="keyword">false</span>);</span><br><span class="line">            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,</span><br><span class="line">                    userLeaving, prev.configChangeFlags, dontWait);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">// Ignore exception, if process died other code will cleanup.</span></span><br><span class="line">            Slog.w(TAG, <span class="string">"Exception thrown during pause"</span>, e);</span><br><span class="line">            mPausingActivity = <span class="keyword">null</span>;</span><br><span class="line">            mLastPausedActivity = <span class="keyword">null</span>;</span><br><span class="line">            mLastNoHistoryActivity = <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mPausingActivity = <span class="keyword">null</span>;</span><br><span class="line">        mLastPausedActivity = <span class="keyword">null</span>;</span><br><span class="line">        mLastNoHistoryActivity = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这里有一个很重要的地方就是<code>prev.app.thread</code>，其实他就是一个IApplicationThread类型的对象，而ApplicationThread则是ActivityThread的一个内部类，它继承了IApplicationThread，并且都是Binder对象，所以说Appcation是一个客户端,而ActivityThread中是一个服务端，到现在为止，Activity的调用来到了ActivityThread中，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-05Activity/activity_2.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><h1 id="在ActivityThread中pause掉当前Activity"><a href="#在ActivityThread中pause掉当前Activity" class="headerlink" title="在ActivityThread中pause掉当前Activity"></a>在ActivityThread中pause掉当前Activity</h1><p>在ActivityThread中则是调用了schedulePauseActivity来执行pause操作：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">schedulePauseActivity</span><span class="params">(IBinder token, <span class="keyword">boolean</span> finished,<span class="keyword">boolean</span> userLeaving, <span class="keyword">int</span> configChanges, <span class="keyword">boolean</span> dontReport)</span> </span>&#123;</span><br><span class="line">    sendMessage(</span><br><span class="line">        finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,</span><br><span class="line">        token,</span><br><span class="line">        (userLeaving ? <span class="number">1</span> : <span class="number">0</span>) | (dontReport ? <span class="number">2</span> : <span class="number">0</span>),</span><br><span class="line">        configChanges);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到在schedulePauseActivity中则是通过handler来发送消息，消息类型为PAUSE_ACTIVITY_FINISHING，那接下来就应该看收到消息之后如何来处理了，<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handlePauseActivity</span><span class="params">(IBinder token, <span class="keyword">boolean</span> finished,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> userLeaving, <span class="keyword">int</span> configChanges, <span class="keyword">boolean</span> dontReport)</span> </span>&#123;</span><br><span class="line">    ActivityClientRecord r = mActivities.get(token);</span><br><span class="line">    <span class="keyword">if</span> (r != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);</span></span><br><span class="line">        <span class="keyword">if</span> (userLeaving) &#123;</span><br><span class="line">            performUserLeavingActivity(r);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        r.activity.mConfigChangeFlags |= configChanges;</span><br><span class="line">        performPauseActivity(token, finished, r.isPreHoneycomb());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Make sure any pending writes are now committed.</span></span><br><span class="line">        <span class="keyword">if</span> (r.isPreHoneycomb()) &#123;</span><br><span class="line">            QueuedWork.waitToFinish();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Tell the activity manager we have paused.</span></span><br><span class="line">        <span class="keyword">if</span> (!dontReport) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ActivityManagerNative.getDefault().activityPaused(token);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException ex) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        mSomeActivitiesChanged = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到在方法内部通过调用performPauseActivity方法来实现对当前Activity的onPause生命周期方法的回调，具体是调用了performPause方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">performPause</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mDoReportFullyDrawn = <span class="keyword">false</span>;</span><br><span class="line">    mFragments.dispatchPause();</span><br><span class="line">    mCalled = <span class="keyword">false</span>;</span><br><span class="line">    onPause();</span><br><span class="line">    mResumed = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">if</span> (!mCalled &amp;&amp; getApplicationInfo().targetSdkVersion</span><br><span class="line">            &gt;= android.os.Build.VERSION_CODES.GINGERBREAD) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> SuperNotCalledException(</span><br><span class="line">                <span class="string">"Activity "</span> + mComponent.toShortString() +</span><br><span class="line">                <span class="string">" did not call through to super.onPause()"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    mResumed = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到最终是调用了Activity的onPause()方法，接着我们回到handlePauseActivity的第二个方法<code>ActivityManagerNative.getDefault().activityPaused(token)</code>，这是应用进程告诉服务进程，当前的Activity已经执行完成onPause方法了，其最后会调用completePauseLocked方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">completePauseLocked</span><span class="params">(<span class="keyword">boolean</span> resumeNext)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> (resumeNext) &#123;</span><br><span class="line">        <span class="keyword">final</span> ActivityStack topStack = mStackSupervisor.getFocusedStack();</span><br><span class="line">        <span class="keyword">if</span> (!mService.isSleepingOrShuttingDown()) &#123;</span><br><span class="line">            mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, <span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mStackSupervisor.checkReadyForSleepLocked();</span><br><span class="line">            ActivityRecord top = topStack.topRunningActivityLocked(<span class="keyword">null</span>);</span><br><span class="line">            <span class="keyword">if</span> (top == <span class="keyword">null</span> || (prev != <span class="keyword">null</span> &amp;&amp; top != prev)) &#123;</span><br><span class="line">                <span class="comment">// If there are no more activities available to run,</span></span><br><span class="line">                <span class="comment">// do resume anyway to start something.  Also if the top</span></span><br><span class="line">                <span class="comment">// activity on the stack is not the just paused activity,</span></span><br><span class="line">                <span class="comment">// we need to go ahead and resume it to ensure we complete</span></span><br><span class="line">                <span class="comment">// an in-flight app switch.</span></span><br><span class="line">                mStackSupervisor.resumeTopActivitiesLocked(topStack, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到经过了一系列的逻辑之后，又调用了resumeTopActivitiesLocked方法，而在该方法中则是调用了startSpecificActivityLocked 来启动新的Activity。来看看目前的流程图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-05Activity/activity_3.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><h1 id="启动新的Activity"><a href="#启动新的Activity" class="headerlink" title="启动新的Activity"></a>启动新的Activity</h1><p>在startSpecificActivityLocked方法中，其实现细节则是和调用Activity的pause方法一样，都是通过Handler机制，发送一个启动Activity的消息，接着处理该消息最后启动Activity。其调用的是performLaunchActivity方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Activity <span class="title">performLaunchActivity</span><span class="params">(ActivityClientRecord r, Intent customIntent)</span> </span>&#123;</span><br><span class="line">Activity activity = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();</span><br><span class="line">            activity = mInstrumentation.newActivity(</span><br><span class="line">                    cl, component.getClassName(), r.intent);</span><br><span class="line">            StrictMode.incrementExpectedActivityCount(activity.getClass());</span><br><span class="line">            r.intent.setExtrasClassLoader(cl);</span><br><span class="line">            r.intent.prepareToEnterProcess();</span><br><span class="line">            <span class="keyword">if</span> (r.state != <span class="keyword">null</span>) &#123;</span><br><span class="line">                r.state.setClassLoader(cl);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!mInstrumentation.onException(activity, e)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(</span><br><span class="line">                    <span class="string">"Unable to instantiate activity "</span> + component</span><br><span class="line">                    + <span class="string">": "</span> + e.toString(), e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        activity.mCalled = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (r.isPersistable()) &#123;</span><br><span class="line">           mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           mInstrumentation.callActivityOnCreate(activity, r.state);</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (!r.activity.mFinished) &#123;</span><br><span class="line">                    activity.performStart();</span><br><span class="line">                    r.stopped = <span class="keyword">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">return</span> activity;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>可以看到最终的Activity对象终于创建出来了，可以看到其过程是使用反射机制创建的，而反射机制在Android系统中的应用也是随处可见。在接下来的过程中还会继续执行Activity的onCreate等一系列的生命周期方法。<br>最后再来看一下整个过程最终的流程图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-05Activity/activity_4.png" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;大家好，今天想与大家一起分享的是Activity。我们平时接触的最多的就是Activity了，作为四大组件中最为重要的老大，Activity究竟是如何启动的呢？这篇文章将会从源码的角度为大家进行全方位的解析，为了方便大家理解整个的过程，我会用流程图的方式将整个过程串起来，希望对大家有所帮助。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Android源码解析" scheme="http://www.wensibo.top/tags/Android%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
    
      <category term="Activity" scheme="http://www.wensibo.top/tags/Activity/"/>
    
  </entry>
  
  <entry>
    <title>从Android源码的角度分析Binder机制</title>
    <link href="http://www.wensibo.top/2017/07/03/Binder/"/>
    <id>http://www.wensibo.top/2017/07/03/Binder/</id>
    <published>2017-07-03T04:01:25.000Z</published>
    <updated>2018-10-21T08:26:20.588Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>大家好，好久不见，距离上篇文章已经有35天之久了，因为身体不舒服害了一场病，不过现在已经好多了；另外这个月是考试月，当然得花多点时间复习功课了；再者这段时间依旧在看书，同时也在研究Android源码，准备了不少干货想与大家一起分享。7月刚到，该放假的也都差不多放假了，该实习的也已经在实习了，而我。。。还是准备秋招吧！多看书多打码多提升自己的眼界。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/Android.jpg" alt="Binder" title="">                </div>                <div class="image-caption">Binder</div>            </figure><a id="more"></a> <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>今天想要和大家一起分享的是Android中的Binder机制，讲真这绝对是Android中很深奥的一个点，如果能够彻底弄懂它，这对初级程序员来说绝对会是一件具有里程碑意义的事件，当然我也研究了许久，终于琢磨出点所以然，所以就拿出来和大家一起分享分享。另外这篇文章将会通过一个小实例来讲解Binder，大家可以点击<a href="https://github.com/Wensibob/AIDLTest" target="_blank" rel="noopener">这里</a>，也欢迎大家fork和star。话不多说让我们开始吧！</p><h1 id="IPC"><a href="#IPC" class="headerlink" title="IPC"></a>IPC</h1><p>为了弄懂IPC的来龙去脉，我将从以下三个方面为大家来讲解，希望对大家理解IPC会有帮助</p><h2 id="什么是IPC"><a href="#什么是IPC" class="headerlink" title="什么是IPC"></a>什么是IPC</h2><p>IPC是<code>Inter Process Communication</code>的缩写，其意思就是进程间的通信，也就是两个进程之间的通信过程。我们都知道在Android系统中，每个应用都运行在一个进程上，具有自己的DVM实例，而且进程之间是相互隔离的，也就是说各个进程之间的数据是互相独立，互不影响的，而如果一个进程崩溃了，也不会影响到另一个进程。<br>采取这样的设计是有一定道理的，例如这样的前提下将互相不影响的系统功能分拆到不同的进程里面去，有助于提升系统的稳定性，毕竟我们都不想自己的应用进程崩溃会导致整个手机系统的崩溃。<br>进程之间隔离是不错的选择，可是如果进程之间想要互相通信，进行数据交互的时候那该怎么办呢？例如我们在自己的应用中想要访问手机通讯录中的联系人，很显然这是两个不同的进程，如果Android没有提供一种进程之间交流的机制，那么这种功能将无法实现。<br>不过由于Android系统使用的是Linux内核，而在Linux系统中进程之间的交互是有一套机制的，所以Android也借鉴了其中的一些机制，从而形成了Android的IPC机制。<br>上面只是粗略的讲解了IPC是啥，关于它的使用和原理我将一一为大家呈上。</p><h2 id="为什么要用IPC"><a href="#为什么要用IPC" class="headerlink" title="为什么要用IPC"></a>为什么要用IPC</h2><p>上一点中我举了访问手机通讯录的例子。但你可能觉得我不需要用到这种功能，那么我就不用管IPC啦！其实不然，IPC在我们的应用开发过程中随处可见，下面我将举一个例子来说明他的重要性。<br>我们在MainActivity中修改一个静态变量，接着在另一个进程的SecondActivity中去访问该变量，看看能否读取已经修改过的变量。</p><p><strong>1、新建一个Student类，并声明一个静态变量</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Student</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String name=<span class="string">"BOB"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2、在MainActivity的onCreate方法中修改name的值，并打印log</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">    setContentView(R.layout.activity_main);</span><br><span class="line">    Student.name = <span class="string">"JACK"</span>;</span><br><span class="line">    Log.d(<span class="string">"MainActivity:Sname="</span>, Student.name);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>3、将SecondActivity设置为新进程，并在其onCreate方法中访问name</strong><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 在清单文件中通过android:process属性为SecondActivity指定特定的进程：com.bob.aidltest：second --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">activity</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">android:name</span>=<span class="string">".SecondActivity"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:process</span>=<span class="string">":second"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">activity</span>&gt;</span></span><br></pre></td></tr></table></figure></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SecondActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(@Nullable Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.second_activity);</span><br><span class="line">        Log.d(<span class="string">"SecondActivity:Sname="</span> , Student.name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>4、通过log，可以看到在MainActivity中修改了name的值，但是在SecondActivity中却无法读取修改后的值</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/name_log.png" alt="Log图" title="">                </div>                <div class="image-caption">Log图</div>            </figure></p><p>通过以上的实验，大家应该明白了一点：在不同的进程之间访问同一个静态变量是行不通的。其原因是：<strong>每一个进程都分配有一个独立的虚拟机，不同的虚拟机在内存分配上有不同的地址空间，这就导致在不同的虚拟机上访问同一个对象会产生多个副本。例如我们在MainActivity中访问的name的值只会影响当前进程，而对其他进程不会造成影响，所以在SecondActivity中访问name时依旧只能访问自己进程中的副本。</strong></p><h2 id="Android中解决IPC的方法"><a href="#Android中解决IPC的方法" class="headerlink" title="Android中解决IPC的方法"></a>Android中解决IPC的方法</h2><p>上面也讲到，为了解决这些跨进程的问题，Android沿用了一些Linux的进程管理机制，使得进程之间能够进行交互，下面我将列出一些常见的IPC方式，需要指出的是本文主要讲解Binder机制，所以会注重讲解AIDL，其他方式请读者自行查阅相关资料。</p><table><thead><tr><th style="text-align:left">名称</th><th style="text-align:left">特点</th><th style="text-align:left">使用场景</th></tr></thead><tbody><tr><td style="text-align:left">Bundle</td><td style="text-align:left">只能传输实现了Serializable或者Parcelable接口或者一些Android支持的特殊对象</td><td style="text-align:left">适合用于四大组件之间的进程交互</td></tr><tr><td style="text-align:left">文件</td><td style="text-align:left">不能做到进程间的即时通信，并且不适合用于高并发的场景</td><td style="text-align:left">适合用于SharedPreference以及IO操作</td></tr><tr><td style="text-align:left">ContentProvider</td><td style="text-align:left">可以访问较多的数据，支持一对多的高并发访问，因为ContentProvider已经自动做好了关于并发的机制</td><td style="text-align:left">适合用于一对多的数据共享并且需要对数据进行频繁的CRUD操作</td></tr><tr><td style="text-align:left">Socket</td><td style="text-align:left">通过网络传输字节流，支持一对多的实时通信，但是实现起来比较复杂</td><td style="text-align:left">适合用于网络数据共享</td></tr><tr><td style="text-align:left">Messenger</td><td style="text-align:left">底层原理是AIDL，只是对其做了封装，但是不能很好的处理高并发的场景，并且传输的数据只能支持Bundle类型</td><td style="text-align:left">低并发的一对多的即时通信</td></tr><tr><td style="text-align:left">AIDL</td><td style="text-align:left">功能强大，使用Binder机制(接下来会讲解),支持一对多的高并发实时通信，但是需要处理好线程同步</td><td style="text-align:left">一对多并且有远程进程通信的场景</td></tr></tbody></table><h1 id="Binder"><a href="#Binder" class="headerlink" title="Binder"></a>Binder</h1><p>终于来到这篇文章的重头戏了，上面讲到Android解决IPC的方法中有一种是AIDL，它使用的原理就是Binder，只有理解了Binder，我们才算是理解了Android跨进程通信的原理。在这里我会带大家看看Android中有哪一些重要的地方使用到了Binder，接着我们会通过一个实例来了解如何使用Binder，最后我们会分析Binder的源码来理解他的工作流程。</p><h2 id="Binder在Android中的运用"><a href="#Binder在Android中的运用" class="headerlink" title="Binder在Android中的运用"></a>Binder在Android中的运用</h2><p>说起Binder在Android的使用场景，可以说是无处不在，我列出一些最常见的场景：</p><ul><li>四大组件的生命周期都是使用Binder机制进行管理的</li><li>View的工作原理也使用了Binder</li><li>WindowManager的工作机制同样使用了Binder</li></ul><p>以上三个方面只是最常见的场景，但是却几乎包括了我们开发的整个流程。我们开发的应用都离不开四大组件，而四大组件也正是依靠Binder机制运行的；对于我们最常见的View，他是如何显示的，View又是如何响应我们的动作的，这其中也用到了Binder(<strong>关于这些内容我会在后续的文章中为大家分析</strong>)。可以说了解Binder对于我们的开发是很有帮助的，那接下来我们就来看看我们该如何使用Binder进行进程间的通信吧！</p><h2 id="如何使用Binder"><a href="#如何使用Binder" class="headerlink" title="如何使用Binder"></a>如何使用Binder</h2><p>现在我们需要实现这样的功能：<strong>客户端与服务端位于不同的进程，客户端需要向服务端添加学生，同时客户端还可以向服务端发起查询学生列表的请求</strong>。</p><h3 id="1、创建Student-java，Student-aidl，IStudentManager-aidl"><a href="#1、创建Student-java，Student-aidl，IStudentManager-aidl" class="headerlink" title="1、创建Student.java，Student.aidl，IStudentManager.aidl"></a>1、创建Student.java，Student.aidl，IStudentManager.aidl</h3><ul><li>Student.java</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.bob.aidltest.aidl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.os.Parcel;</span><br><span class="line"><span class="keyword">import</span> android.os.Parcelable;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Created by bob on 17-7-3.</span></span><br><span class="line"><span class="comment"> * 所有需要在Binder传递的数据类型都需要实现Parcelable接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Student</span> <span class="keyword">implements</span> <span class="title">Parcelable</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String name=<span class="string">"BOB"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> s_id;</span><br><span class="line">    <span class="keyword">public</span> String s_name;</span><br><span class="line">    <span class="keyword">public</span> String s_gender;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Student</span><span class="params">(Parcel in)</span> </span>&#123;</span><br><span class="line">        s_id = in.readInt();</span><br><span class="line">        s_name = in.readString();</span><br><span class="line">        s_gender = in.readString();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Student</span><span class="params">(<span class="keyword">int</span> s_id, String s_name, String s_gender)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.s_id = s_id;</span><br><span class="line">        <span class="keyword">this</span>.s_name = s_name;</span><br><span class="line">        <span class="keyword">this</span>.s_gender = s_gender;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">describeContents</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">writeToParcel</span><span class="params">(Parcel dest, <span class="keyword">int</span> flags)</span> </span>&#123;</span><br><span class="line">        dest.writeInt(s_id);</span><br><span class="line">        dest.writeString(s_name);</span><br><span class="line">        dest.writeString(s_gender);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Creator&lt;Student&gt; CREATOR = <span class="keyword">new</span> Creator&lt;Student&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Student <span class="title">createFromParcel</span><span class="params">(Parcel in)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Student(in);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Student[] newArray(<span class="keyword">int</span> size) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Student[size];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"[StudentID: %s , StudentName: %s , StudentGender: %s]"</span>, s_id, s_name, s_gender);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>Student.aidl</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Student1.aidl</span></span><br><span class="line"><span class="keyword">package</span> com.bob.aidltest.aidl;</span><br><span class="line"></span><br><span class="line">parcelable Student;</span><br></pre></td></tr></table></figure><ul><li>IStudentManager.aidl</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// IStudentManager.aidl</span></span><br><span class="line"><span class="keyword">package</span> com.bob.aidltest.aidl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.bob.aidltest.aidl.Student;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">IStudentManager</span> </span>&#123;</span><br><span class="line">    <span class="function">List&lt;Student&gt; <span class="title">getStudentList</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(in Student student)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建完毕之后手动编译项目(<code>Build--&gt;ReBuild Project</code>)，接着就会在<code>app/build/generated/source/aidl/debug/com/bob/aidltest/aidl/IStudentManager.java</code>中看到自动生成的<code>IStudentManager</code>接口</strong>，如下图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/IStudentManager.png" alt="IStudentManager" title="">                </div>                <div class="image-caption">IStudentManager</div>            </figure></p><h3 id="2、分析IStudentManager-java"><a href="#2、分析IStudentManager-java" class="headerlink" title="2、分析IStudentManager.java"></a>2、分析IStudentManager.java</h3><p>先来看看自动生成的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IStudentManager</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">IInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">/** 内部类Stub，继承自Binder并且实现了IStudentManager接口，因此他也是一个Binder对象，这个内部类是需要在服务端手动实现的，并且会通过onBind方法返回给客户端 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Stub</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">bob</span>.<span class="title">aidltest</span>.<span class="title">aidl</span>.<span class="title">IStudentManager</span></span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> java.lang.String DESCRIPTOR = <span class="string">"com.bob.aidltest.aidl.IStudentManager"</span>;</span><br><span class="line">        <span class="comment">/** 构造方法 */</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Stub</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.attachInterface(<span class="keyword">this</span>, DESCRIPTOR);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 将服务端的Binder对象转换为客户端的所需的AIDL接口类型的对象，客户端拿到这个对象就可以通过这个对象远程访问服务端的方法</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> com.bob.aidltest.aidl.<span class="function">IStudentManager <span class="title">asInterface</span><span class="params">(android.os.IBinder obj)</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> ((obj==<span class="keyword">null</span>)) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);</span><br><span class="line">            <span class="keyword">if</span> (((iin!=<span class="keyword">null</span>)&amp;&amp;(iin <span class="keyword">instanceof</span> com.bob.aidltest.aidl.IStudentManager))) &#123;</span><br><span class="line">                <span class="keyword">return</span> ((com.bob.aidltest.aidl.IStudentManager)iin);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> com.bob.aidltest.aidl.IStudentManager.Stub.Proxy(obj);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 运行在服务端进程的Binder线程池中；当客户端进程发起远程请求时，远程请求会要求系统底层执行回调该方法</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> code 客户端进程请求方法标识符。服务端进程会根据该标识确定所请求的目标方法</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> data 目标方法的参数，他是客户端进程传进来的，当我们调用addStudent(Student student)方法时，参数就是Student对象</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> reply 目标方法执行后的结果，将会返回给客户端，例如当我们调用getStudentList，返回的就是一个Student的列表</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTransact</span><span class="params">(<span class="keyword">int</span> code, android.os.Parcel data, android.os.Parcel reply, <span class="keyword">int</span> flags)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="keyword">switch</span> (code)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">case</span> INTERFACE_TRANSACTION:</span><br><span class="line">                &#123;</span><br><span class="line">                    reply.writeString(DESCRIPTOR);</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> TRANSACTION_getStudentList:</span><br><span class="line">                &#123;</span><br><span class="line">                    data.enforceInterface(DESCRIPTOR);</span><br><span class="line">                    java.util.List&lt;com.bob.aidltest.aidl.Student&gt; _result = <span class="keyword">this</span>.getStudentList();</span><br><span class="line">                    reply.writeNoException();</span><br><span class="line">                    reply.writeTypedList(_result);</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> TRANSACTION_addStudent:</span><br><span class="line">                &#123;</span><br><span class="line">                    data.enforceInterface(DESCRIPTOR);</span><br><span class="line">                    com.bob.aidltest.aidl.Student _arg0;</span><br><span class="line">                    <span class="keyword">if</span> ((<span class="number">0</span>!=data.readInt())) &#123;</span><br><span class="line">                        _arg0 = com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> &#123;</span><br><span class="line">                        _arg0 = <span class="keyword">null</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">this</span>.addStudent(_arg0);</span><br><span class="line">                    reply.writeNoException();</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">super</span>.onTransact(code, data, reply, flags);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 代理的内部类，他实现了IStudentManager接口，这个代理类就是服务端返回给客户端的AIDL接口对象，客户端可以通过这个代理类访问服务端的方法</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Proxy</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">bob</span>.<span class="title">aidltest</span>.<span class="title">aidl</span>.<span class="title">IStudentManager</span></span></span><br><span class="line"><span class="class">        </span>&#123;</span><br><span class="line">            <span class="keyword">private</span> android.os.IBinder mRemote;</span><br><span class="line">            Proxy(android.os.IBinder remote)</span><br><span class="line">            &#123;</span><br><span class="line">                mRemote = remote;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function">            </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> mRemote;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">public</span> java.lang.<span class="function">String <span class="title">getInterfaceDescriptor</span><span class="params">()</span></span></span><br><span class="line"><span class="function">            </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> DESCRIPTOR;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="meta">@Override</span> <span class="keyword">public</span> java.util.List&lt;com.bob.aidltest.aidl.Student&gt; getStudentList() <span class="keyword">throws</span> android.os.RemoteException</span><br><span class="line">            &#123;</span><br><span class="line">                android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line">                android.os.Parcel _reply = android.os.Parcel.obtain();</span><br><span class="line">                java.util.List&lt;com.bob.aidltest.aidl.Student&gt; _result;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    _data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, <span class="number">0</span>);</span><br><span class="line">                    _reply.readException();</span><br><span class="line">                    _result = _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">finally</span> &#123;</span><br><span class="line">                    _reply.recycle();</span><br><span class="line">                    _data.recycle();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> _result;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(com.bob.aidltest.aidl.Student student)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function">            </span>&#123;</span><br><span class="line">                android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line">                android.os.Parcel _reply = android.os.Parcel.obtain();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    _data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">                    <span class="keyword">if</span> ((student!=<span class="keyword">null</span>)) &#123;</span><br><span class="line">                        _data.writeInt(<span class="number">1</span>);</span><br><span class="line">                        student.writeToParcel(_data, <span class="number">0</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> &#123;</span><br><span class="line">                        _data.writeInt(<span class="number">0</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, <span class="number">0</span>);</span><br><span class="line">                    _reply.readException();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">finally</span> &#123;</span><br><span class="line">                    _reply.recycle();</span><br><span class="line">                    _data.recycle();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> java.util.List&lt;com.bob.aidltest.aidl.Student&gt; getStudentList() <span class="keyword">throws</span> android.os.RemoteException;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(com.bob.aidltest.aidl.Student student)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可能看了上面的注释大家还是一头雾水，那就先看看这个类的结构图吧：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/IStudentManager_structure.png" alt="IStudentManager结构图" title="">                </div>                <div class="image-caption">IStudentManager结构图</div>            </figure><br>有关这个类的细节我们待会讲，现在只需要知道我们需要在服务端手动实现Proxy类并实现其中的方法。</p><h3 id="创建StudentManagerService-java，并为其指定进程"><a href="#创建StudentManagerService-java，并为其指定进程" class="headerlink" title="创建StudentManagerService.java，并为其指定进程"></a>创建StudentManagerService.java，并为其指定进程</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Created by bob on 17-7-3.</span></span><br><span class="line"><span class="comment"> * 服务端代码</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StudentManagerService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"StudentManagerService"</span>;</span><br><span class="line">    <span class="comment">//判断Service是否销毁</span></span><br><span class="line">    <span class="keyword">private</span> AtomicBoolean mIsServiceDestroyed = <span class="keyword">new</span> AtomicBoolean(<span class="keyword">false</span>);</span><br><span class="line">    <span class="comment">//适合用于进程间传输的列表类</span></span><br><span class="line">    <span class="keyword">private</span> CopyOnWriteArrayList&lt;Student&gt; mStudentList = <span class="keyword">new</span> CopyOnWriteArrayList&lt;Student&gt;();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate();</span><br><span class="line">        <span class="comment">//在服务端手动添加两位默认的学生</span></span><br><span class="line">        mStudentList.add(<span class="keyword">new</span> Student(<span class="number">1</span>, <span class="string">"BOB"</span>, <span class="string">"man"</span>));</span><br><span class="line">        mStudentList.add(<span class="keyword">new</span> Student(<span class="number">2</span>, <span class="string">"MAY"</span>, <span class="string">"woman"</span>));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> IBinder <span class="title">onBind</span><span class="params">(Intent intent)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mBinder;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mIsServiceDestroyed.set(<span class="keyword">false</span>);</span><br><span class="line">        <span class="keyword">super</span>.onDestroy();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Binder mBinder = <span class="keyword">new</span> IStudentManager.Stub() &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> List&lt;Student&gt; <span class="title">getStudentList</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">            SystemClock.sleep(<span class="number">5000</span>);<span class="comment">//休眠5s模拟耗时操作</span></span><br><span class="line">            <span class="keyword">return</span> mStudentList;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(Student student)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">            mStudentList.add(student);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在清单文件中指定服务的进程</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">service</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">android:name</span>=<span class="string">".StudentManagerService"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:process</span>=<span class="string">":remote"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">service</span>&gt;</span></span><br></pre></td></tr></table></figure><p>可以看到这个服务类跟普通的服务类相差并不大，唯一的区别在于它创建了一个IStudentManager.Stub的匿名内部类并且实现了其中的方法，在onBind方法中将这个IBinder对象返回给客户端。<strong>这里需要说明一下：Binder是实现了IBinder接口的，所以他同时也是一个IBinder对象。</strong></p><h3 id="在客户端愉快的绑定Service吧！"><a href="#在客户端愉快的绑定Service吧！" class="headerlink" title="在客户端愉快的绑定Service吧！"></a>在客户端愉快的绑定Service吧！</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"MainActivity_Client"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MESSAGE_QUERY_STUDENTLIST=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> student_size = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> IStudentManager mRemoteStudentManager;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ServiceConnection mConnection=<span class="keyword">new</span> ServiceConnection() &#123;</span><br><span class="line">        <span class="comment">//onServiceConnected与onServiceDisconnected都是在主线程中的，所以如果里面如果涉及到服务端的耗时操作那么需要在子线程中进行</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceConnected</span><span class="params">(ComponentName name, IBinder service)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//获取到IStudentManager对象</span></span><br><span class="line">            <span class="keyword">final</span> IStudentManager studentManager =IStudentManager.Stub.asInterface(service);</span><br><span class="line">            mRemoteStudentManager = studentManager;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceDisconnected</span><span class="params">(ComponentName name)</span> </span>&#123;</span><br><span class="line">            mRemoteStudentManager = <span class="keyword">null</span>;</span><br><span class="line">            Log.d(TAG, <span class="string">"onServiceDisconnected.threadname:"</span> + Thread.currentThread().getName());</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">        Intent intent = <span class="keyword">new</span> Intent(<span class="keyword">this</span>, StudentManagerService.class);</span><br><span class="line">        bindService(intent, mConnection, BIND_AUTO_CREATE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        unbindService(mConnection);</span><br><span class="line">        <span class="keyword">super</span>.onDestroy();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将服务端返回的数据显示在界面上</span></span><br><span class="line">    <span class="keyword">private</span> Handler mHandler=<span class="keyword">new</span> Handler()&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">                <span class="keyword">case</span> MESSAGE_QUERY_STUDENTLIST:</span><br><span class="line">                    Toast.makeText(MainActivity.<span class="keyword">this</span>, msg.obj.toString(),Toast.LENGTH_SHORT).show();</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="keyword">super</span>.handleMessage(msg);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在客户端向服务端添加一名学生</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mRemoteStudentManager != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                <span class="keyword">int</span> student_id = student_size+ <span class="number">1</span>;</span><br><span class="line">                Student newStudent;</span><br><span class="line">                <span class="keyword">if</span> (student_id % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                    newStudent= <span class="keyword">new</span> Student(student_id, <span class="string">"新学生"</span> + student_id, <span class="string">"man"</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    newStudent= <span class="keyword">new</span> Student(student_id, <span class="string">"新学生"</span> + student_id, <span class="string">"woman"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                mRemoteStudentManager.addStudent(newStudent);</span><br><span class="line">                Log.d(TAG, <span class="string">"添加一位学生："</span> + newStudent.toString());</span><br><span class="line">            &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在客户端向服务端发起查询学生的请求</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">get_student_list</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        Toast.makeText(<span class="keyword">this</span>, <span class="string">"正在获取学生列表"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">        <span class="comment">//由于服务端的查询操作是耗时操作，所以客户端需要开启子线程进行工作</span></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (mRemoteStudentManager != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">try</span>&#123;</span><br><span class="line">                        <span class="keyword">final</span> List&lt;Student&gt; students = mRemoteStudentManager.getStudentList();</span><br><span class="line">                        student_size = students.size();</span><br><span class="line">                        Log.d(TAG, <span class="string">"从服务器成功获取到学生列表:"</span> + students.toString());</span><br><span class="line">                        mHandler.obtainMessage(MESSAGE_QUERY_STUDENTLIST, students).sendToTarget();</span><br><span class="line">                    &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到我们在客户端只需要绑定远程的服务端，服务端就会返回一个IBinder对象，接着我们需要调用IStudentManager.Stub.asInterface()方法，将这个IBinder对象转换为我们客户端可用的AIDL接口对象，拿到这个对象之后我们就可以远程调用服务端的方法了。是不是很容易？<br><strong>但是需要注意的一点是为了模拟耗时操作，我们在服务端的getStudentList的方法中使用休眠以模拟耗时操作，所以客户端在调用该方法时不能直接在主线程中调用，而是应该开启一个子线程，在子线程中调用这个耗时的操作。</strong></p><h3 id="看看效果"><a href="#看看效果" class="headerlink" title="看看效果"></a>看看效果</h3><p>首先我们获取学生列表，接着连续添加4个学生，再次查看学生列表，最终的结果如下图，可以看到我们已经实现了两个进程之间的交互，接下来我们将分析Binder的原理。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/client_log.png" alt="结果图" title="">                </div>                <div class="image-caption">结果图</div>            </figure></p><h2 id="Binder的原理"><a href="#Binder的原理" class="headerlink" title="Binder的原理"></a>Binder的原理</h2><h3 id="进程的机制"><a href="#进程的机制" class="headerlink" title="进程的机制"></a>进程的机制</h3><p>首先我们需要了解进程之间为什么不能直接进行通信，以下是两个进程的示意图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/process.png" alt="进程间通信示意图" title="">                </div>                <div class="image-caption">进程间通信示意图</div>            </figure><br>从上面的图我们可以得到以下几点：</p><ul><li>一个进程空间分为：用户态和内核态，即把进程内用户和内核隔离开来</li><li>进程之间，由于Android系统为每个进程分配了一个独立的虚拟机，用户空间和内核空间的数据不可交互</li><li>Binder作为进程间的介质，充当了中介，使得进程间的内核态可以通过Binder进行数据交互</li></ul><h3 id="IPC交互示意图"><a href="#IPC交互示意图" class="headerlink" title="IPC交互示意图"></a>IPC交互示意图</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/IPC.png" alt="IPC交互示意图" title="">                </div>                <div class="image-caption">IPC交互示意图</div>            </figure><p>图中总共有四个元素，分别是充当客户端的Activity，服务端的StudentManagerService，充当服务管理者的IStudentManager以及充当访问介质的Binder驱动。他们的职责如下：</p><ul><li><strong>StudentManagerService:</strong> 服务提供者，这里面会有许多我们常用的服务，在本例中提供的服务就是添加学生以及获取学生列表。而在系统中则包括有ActivityService 、 WindowMananger等服务，这些系统服务提供的功能，对四大组件以及Window的工作提供的保障。</li><li><strong>Activity:</strong> 服务调用者，一般就是我们的应用，在这里我们通过调用StudentManagerService的服务来完成工作。</li><li><strong>IStudentManager:</strong> 他是负责管理服务的，在其内部通过map集合来存储Service与Binder的映射关系，这样客户端在向其请求服务的时候就能够返回特定的Binder。</li><li><strong>Binder驱动:</strong> 他是IStudentManager连接各种Service的桥梁，同时也是客户端与服务端交流的桥梁。</li></ul><p><strong>总结起来说，应用程序(<code>Activity</code>)首先向<code>IStudentManager</code>发送请求<code>StudentManagerService</code>的服务，<code>IStudentManager</code>查看已经注册在里面的服务的列表，找到相应的服务后，通过<code>Binder驱动</code>将其中的<code>Binder</code>对象返回给客户端，从而完成对服务的请求。</strong></p><h3 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h3><p>我们主要分析的就是<code>IStudentManager</code>这个类，从上面得到讲解我们已经知道它包含了两个类：Stub和Proxy。先来看看Proxy类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Proxy.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> java.util.List&lt;com.bob.aidltest.aidl.Student&gt; getStudentList() <span class="keyword">throws</span> android.os.RemoteException</span><br><span class="line">&#123;</span><br><span class="line">    android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line">    android.os.Parcel _reply = android.os.Parcel.obtain();</span><br><span class="line">    java.util.List&lt;com.bob.aidltest.aidl.Student&gt; _result;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        _data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">        mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, <span class="number">0</span>);</span><br><span class="line">        _reply.readException();</span><br><span class="line">        _result = _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">finally</span> &#123;</span><br><span class="line">        _reply.recycle();</span><br><span class="line">        _data.recycle();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> _result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStudent</span><span class="params">(com.bob.aidltest.aidl.Student student)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line">    android.os.Parcel _reply = android.os.Parcel.obtain();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        _data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">        <span class="keyword">if</span> ((student!=<span class="keyword">null</span>)) &#123;</span><br><span class="line">            _data.writeInt(<span class="number">1</span>);</span><br><span class="line">            student.writeToParcel(_data, <span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            _data.writeInt(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, <span class="number">0</span>);</span><br><span class="line">        _reply.readException();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">finally</span> &#123;</span><br><span class="line">        _reply.recycle();</span><br><span class="line">        _data.recycle();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面截取了Proxy的两个方法，其中Proxy是运行在客户端的，他是用服务端返回来的Binder对象调用了<code>public static IStudentManager asInterface(IBinder obj)</code>方法返回来的。<br>既然Proxy运行在客户端，那么客户端也是通过Proxy来调用远程服务端的方法的，也就是说我们将调用方法需要用到的参数传递给Proxy，接着由Proxy来访问服务端，所以我们能够看到，Proxy将我们的参数写进了_data，而_reply则代表从服务端返回来的结果。<br><strong>从代码中我们还看到客户端在将数据传递给服务端之后就处于阻塞状态，直到服务端返回结果，所以如果调用的服务端方法是一个耗时方法，那么我们就需要在子线程中进行工作了。</strong><br>数据准备好之后当然是需要传递了，可以看到Proxy通过transact方法讲数据传递出去了，接下来就来看transact方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Binder#transact</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">transact</span><span class="params">(<span class="keyword">int</span> code, Parcel data, Parcel reply,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> flags)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">false</span>) Log.v(<span class="string">"Binder"</span>, <span class="string">"Transact: "</span> + code + <span class="string">" to "</span> + <span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (data != <span class="keyword">null</span>) &#123;</span><br><span class="line">            data.setDataPosition(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//调用了Binder的onTransact</span></span><br><span class="line">        <span class="keyword">boolean</span> r = onTransact(code, data, reply, flags);</span><br><span class="line">        <span class="keyword">if</span> (reply != <span class="keyword">null</span>) &#123;</span><br><span class="line">            reply.setDataPosition(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> r;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>可以看到transact方法实际上调用了Binder的onTransact，而这里的Binder就是指Stub了，我们看一下Stub的定义：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Stub</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">bob</span>.<span class="title">aidltest</span>.<span class="title">aidl</span>.<span class="title">IStudentManager</span></span></span><br></pre></td></tr></table></figure></p><p>可以看到Stub确实继承了Binder并且也实现了IStudentManager接口，接下来我们继续看Stub中的onTransact方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTransact</span><span class="params">(<span class="keyword">int</span> code, android.os.Parcel data, android.os.Parcel reply, <span class="keyword">int</span> flags)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (code)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">case</span> INTERFACE_TRANSACTION:</span><br><span class="line">        &#123;</span><br><span class="line">            reply.writeString(DESCRIPTOR);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> TRANSACTION_getStudentList:</span><br><span class="line">        &#123;</span><br><span class="line">            data.enforceInterface(DESCRIPTOR);</span><br><span class="line">            java.util.List&lt;com.bob.aidltest.aidl.Student&gt; _result = <span class="keyword">this</span>.getStudentList();</span><br><span class="line">            reply.writeNoException();</span><br><span class="line">            reply.writeTypedList(_result);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> TRANSACTION_addStudent:</span><br><span class="line">        &#123;</span><br><span class="line">            data.enforceInterface(DESCRIPTOR);</span><br><span class="line">            com.bob.aidltest.aidl.Student _arg0;</span><br><span class="line">            <span class="keyword">if</span> ((<span class="number">0</span>!=data.readInt())) &#123;</span><br><span class="line">                _arg0 = com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                _arg0 = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>.addStudent(_arg0);</span><br><span class="line">            reply.writeNoException();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.onTransact(code, data, reply, flags);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到，服务端通过客户端传递过来的code常量来判断客户端需要调用的是哪个方法，接着就执行该方法，执行完之后如果有数据返回则将结果写入reply，接着Proxy就可以收到结果了。而整个通信过程也就结束了。<br>最后我借用<a href="http://www.jianshu.com/p/4ee3fd07da14" target="_blank" rel="noopener">Carson_Ho</a>的一张流程图来描述这个完整的流程：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-7-03Binder/all_process.png" alt="Binder机制流程图" title="">                </div>                <div class="image-caption">Binder机制流程图</div>            </figure></p><h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>Binder机制在Android中的作用举足轻重，本文只是通过Java代码分析其工作流程，由于博主不擅长C/C++，所以无法研究native，不过本文依旧完整的阐述了Binder的整个运行机制，希望通过本文大家能够对Binder机制有更深刻的理解，关于Binder在Android系统中的更多运用我会在后续的文章中为大家奉上，希望大家多多支持。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;大家好，好久不见，距离上篇文章已经有35天之久了，因为身体不舒服害了一场病，不过现在已经好多了；另外这个月是考试月，当然得花多点时间复习功课了；再者这段时间依旧在看书，同时也在研究Android源码，准备了不少干货想与大家一起分享。7月刚到，该放假的也都差不多放假了，该实习的也已经在实习了，而我。。。还是准备秋招吧！多看书多打码多提升自己的眼界。&lt;/p&gt;
&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2017-7-03Binder/Android.jpg&quot; alt=&quot;Binder&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;Binder&lt;/div&gt;
            &lt;/figure&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Android源码解析" scheme="http://www.wensibo.top/tags/Android%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
    
      <category term="Binder机制" scheme="http://www.wensibo.top/tags/Binder%E6%9C%BA%E5%88%B6/"/>
    
      <category term="IPC" scheme="http://www.wensibo.top/tags/IPC/"/>
    
  </entry>
  
  <entry>
    <title>What kind of PPT makes you an excellent speaker——什么样的PPT能助你成为一个优秀的演讲者</title>
    <link href="http://www.wensibo.top/2017/05/28/speaker/"/>
    <id>http://www.wensibo.top/2017/05/28/speaker/</id>
    <published>2017-05-28T08:07:25.000Z</published>
    <updated>2018-10-21T08:26:20.996Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>今天是端午节假期的第一天，在这里祝大家假期快乐，不过像我这种渣渣就不休息了，撸起袖子继续学习啦😜 <a href="http://wensibo.top/2017/05/15/GankClient/" target="_blank" rel="noopener">上篇文章</a>已经向大家承诺会写一些技术以外的文章，所以这次我也履行诺言跟大家一起来分享一下我在最近的演讲过程的一些心得，当然这里最主要的还是讲如何用PPT(因为我穷得只能吃土所以就无法为大家讲解Keynote啦，但是大体的思想是差不多的)为你的演讲增色，毕竟作为程序员，在工作的时候不仅仅是只面对代码，有的时候如果具备一定的演讲才能，那么你的老板同事对你也会刮目相看。这篇文章纯粹只是我个人的经验总结，请大神轻喷😂<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/jobs.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><br><a id="more"></a> </p><script>console.error("Error: [hexo-tag-aplayer] Unrecognized tag argument(2): autoplay=ture");</script><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>其实这篇文章我很久以前就想写了，但是碍于自己在演讲方面还不是特别成熟所以一直不敢动笔，直到今天，我觉得自己似乎已经有一定的能力可以跟大家一起来分享PPT与演讲的故事了。诚然这篇文章并不是入门的教你如何做PPT，而是与大家一起探讨分享如何将PPT做的引人入胜，为你的演讲助力增色，我也将围绕这个话题与大家一起讲述演讲的前后以及演讲过程中的注意事项，力争将我学习到的经验技巧“和盘托出”😊</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-15GankClient/car.jpg" alt="老铁别废话了" title="">                </div>                <div class="image-caption">老铁别废话了</div>            </figure>        <h1 id="演讲前"><a href="#演讲前" class="headerlink" title="演讲前"></a>演讲前</h1><p>在封面中大家已经看到了伟大的乔帮主，他的确是一个演讲天才，当然这也与他的人格魅力密切相关。不过一场好的演讲还是得需要做足充分准备的，以下我就一一道来。</p><h2 id="提前到演讲场地勘测"><a href="#提前到演讲场地勘测" class="headerlink" title="提前到演讲场地勘测"></a>提前到演讲场地勘测</h2><p>或许大家觉得这个似乎没有必要，但实际上如果你将要准备一个大型的演讲或者你觉得这个演讲对你十分重要的话，那我奉劝你还是照做，为了你的观众你就辛苦一点吧！大家可以来看一下以下两张图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/auditorium_1.jpg" alt="水平式礼堂" title="">                </div>                <div class="image-caption">水平式礼堂</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/auditorium_2.jpg" alt="阶梯式礼堂" title="">                </div>                <div class="image-caption">阶梯式礼堂</div>            </figure><br>很明显如果演讲的场地是像第一张图中的水平式礼堂那样的话，那么坐在后面的观众看屏幕就会比较吃力，尤其当内容处于屏幕的下方时，这里有两种解决方式，第一是与主办方协商将屏幕高度提高一些，第二个就是我们在做PPT的时候适当将内容放到中上部分，避免在PPT下方放置过多的内容。对于第二种演讲场地的话，就可以省去这种担忧了，但是因为我还在学校，除非是大的演讲教室，一般都是水平式的演讲场地。这一点对观众来说是至关重要的，虽然观众可能觉得这应当是理所当然的，但是又有多少演讲者考虑到这一点呢？</p><h2 id="考虑PPT的尺寸"><a href="#考虑PPT的尺寸" class="headerlink" title="考虑PPT的尺寸"></a>考虑PPT的尺寸</h2><p>PPT的尺寸是演讲者必须要十分注意的，往往我们在16:9的电脑屏幕上做的PPT可以完全覆盖整个屏幕，但是到了演讲场地时由于演讲的电脑是4:3或者投影仪同样也是4:3的时候，你辛辛苦苦做好的PPT就会变形，出现意想不到的情况。因此这一点也是你提前到演讲场地勘测时需要询问的一个重要事宜，但是最稳妥的方式依旧是带上自己的电脑，毕竟由于office的版本兼容性存在差异，许多意想不到的状况都有可能随时发生。</p><h2 id="找准你的观众"><a href="#找准你的观众" class="headerlink" title="找准你的观众"></a>找准你的观众</h2><p>演讲分为很多种，有商业性的有娱乐性的，所以演讲的观众也有很多类型，有你的同事同学，有你的老师老板合作伙伴等等，对待不同的场合与观众我们做PPT也应当采取不同的style。例如对待年轻人我们可以用很装逼的PPT来展示我们的演讲，那对于很严肃的场合或者年纪比较大的观众我们就应该更加踏实稳重一点，或许只有尽可能地去“取悦”观众你才会慢慢的成功。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/example1.png" alt="一次搞砸的演讲" title="">                </div>                <div class="image-caption">一次搞砸的演讲</div>            </figure><br>在这里我举个例子，前一次我在班级中演讲，上面是我演讲中的一页幻灯片，演讲的观众是年纪比较大的一个老师以及我的同学们，由于之前我一直坚持我的风格，并且也得到了老师和同学们的认可，但是到了这一次情况就变得有些不一样了，对于年纪比较大的观众，他们或许对我的这种风格很不感冒，这个老师对我的评价就是：“对着两个字说了3分钟”，诚然我没有做足充分的准备，也万万没有想到会遇到众口难调的情况，所以也就有了这次尴尬的经历，不过这也给了我宝贵的经验。</p><h2 id="你的特色"><a href="#你的特色" class="headerlink" title="你的特色"></a>你的特色</h2><p>首先我先向大家展示一下我之前为一个演讲做的一个PPT动画视频的开场，这个开场也获得了在场观众的热烈反响。<br><div id="dplayer0" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer0"),"theme":"#FADFA3","video":{"url":"http://wensibo.top/file/img/2017-5-28speaker/quick.mp4","pic":"http://wensibo.top/file/img/2017-5-28speaker/quick.png"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script><br>其实想跟大家说的是我们做的PPT是展示给观众的，那么我们就要千方百计的“取悦”观众，而最好的方式就是用你的PPT不断地去吸引他们的眼球。在开场前的这种效果绝对是物超所值的，不仅可以装装逼让大家对你刮目相看而且还起到了为演讲暖场的作用，但是切记要分清楚场合，也要分清楚的你的观众是否能够接受这种新奇的事物，不然就不仅仅是画蛇添足而已了。如果你想下载源文件，请戳<a href="https://github.com/Wensibob/Awesome-PPT-Keynote" target="_blank" rel="noopener">这里</a>，记得给我个star哦⭐</p><h2 id="越用心越糟糕"><a href="#越用心越糟糕" class="headerlink" title="越用心越糟糕"></a>越用心越糟糕</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/more_text.png" alt="越用心越糟糕" title="">                </div>                <div class="image-caption">越用心越糟糕</div>            </figure><p>这是我找到的别人做的一页幻灯片，可以看到作者很用心，生怕观众看不懂把每个字都写上来了，但是我们想想效果，观众会去看完这些字吗？看完之后会记住吗？就算记住了能够记住重点吗？显然是不能的，如果换成下面的这页幻灯片呢？<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/less_text.JPEG" alt="换种方式" title="">                </div>                <div class="image-caption">换种方式</div>            </figure><br>第二页幻灯片整个的逼格就不一样了，他给观众的震撼力更强，更加简明扼要的抓住内容的重点，而一些次要的则交给演讲者口头表达出来就行了。当然这张图片除了数字之外还有图片的渲染，这也是接下来要讲的。</p><h2 id="你知道你的图片很丑吗？"><a href="#你知道你的图片很丑吗？" class="headerlink" title="你知道你的图片很丑吗？"></a>你知道你的图片很丑吗？</h2><p>上面的这页幻灯片中背景图片很好看，与我们在度娘中找到的那些简直就是大巫见小巫，当然除了找到了好的图片之外，我们还需要对图片进行修饰。我一直很反感直接将文字贴在一张美美的图片上，要么让观众好好欣赏图片要么就好好看字，千万不要糟蹋图片，那么如何将这两种元素很好的结合呢？上面这页幻灯片用了一个比较普遍的手法，就是为图片加上一个有透明度的文本框，具体的做法就是插入一个文本框，用适当的颜色填充整个文本框并将文本框覆盖住整张图片，接下来调整文本框填充色的透明度就可以达到上面的效果，相信聪明的你应该很容易做到😎</p><h2 id="如何找到好的图片？"><a href="#如何找到好的图片？" class="headerlink" title="如何找到好的图片？"></a>如何找到好的图片？</h2><p>好的图片可以为演讲增色不少，<strong>那么问题来了，如何找到更好的图片呢？</strong></p><ul><li>为大家提供一些有用的图片素材网站：</li></ul><blockquote><ul><li><a href="https://pixabay.com/" target="_blank" rel="noopener">免费图片-Pixabay</a></li><li><a href="https://500px.com/" target="_blank" rel="noopener">最出色的摄影社区-500px</a></li><li><a href="https://www.pexels.com/" target="_blank" rel="noopener">Free stock photos-Pexels</a></li><li><a href="https://dribbble.com/" target="_blank" rel="noopener">设计师的汇集地-Dribbble</a></li><li><a href="https://www.bing.com/images/discover?form=Z9LH" target="_blank" rel="noopener">必应图片-绝对比百度图片强100倍</a></li><li><a href="https://www.google.com/imghp?hl=zh-CN&amp;tab=wi&amp;ei=Z2wqWZH6OOGR0gK4poCQBQ&amp;ved=0EKouCBcoAQ" target="_blank" rel="noopener">Google图片-自备梯子</a></li></ul></blockquote><ul><li>还有图标素材网站：</li></ul><blockquote><ul><li><a href="http://www.iconfont.cn/" target="_blank" rel="noopener">阿里巴巴矢量图标库</a></li><li><a href="https://www.materialpalette.com/icons" target="_blank" rel="noopener">Material Design图标库</a></li><li><a href="http://www.easyicon.net/" target="_blank" rel="noopener">EasyIcon</a></li></ul></blockquote><h2 id="好好记住PPT的内容"><a href="#好好记住PPT的内容" class="headerlink" title="好好记住PPT的内容"></a>好好记住PPT的内容</h2><p>优秀的演讲者应该能够做到脱稿演讲，但是这并无捷径，需要我们按照制作PPT的思路去记忆其中的内容，并且在演讲的时候能够做到行动自如而不是站在电脑旁边看着幻灯片的内容，否则观众就会对你失去兴趣了。我个人的一个小技巧就是在做PPT之前列一个提纲，接着按照这个提纲制作幻灯片(<strong>幻灯片内容的逻辑性必须要强</strong>)，等到你需要背诵的时候只需要回忆你列的提纲，大部分的内容就会呈现在你的脑海中了。</p><h1 id="演讲时"><a href="#演讲时" class="headerlink" title="演讲时"></a>演讲时</h1><h2 id="带上一只翻页笔"><a href="#带上一只翻页笔" class="headerlink" title="带上一只翻页笔"></a>带上一只翻页笔</h2><p>如果你是一个职业的演讲者，你对翻页笔应该不陌生，它们长这样👇<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/wireless_presenter_1.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-28speaker/wireless_presenter_2.jpg" alt="" title="">                </div>                <div class="image-caption"></div>            </figure></p><p>如果你没有翻页笔，那么无线鼠标也是不错的选择，总之有了这些工具你就可以脱离电脑，随心所欲的操纵幻灯片，但是这一切的前提是你已经能够驾驭幻灯片了哦！</p><h2 id="真诚地向观众展示你精心准备了许久的礼物"><a href="#真诚地向观众展示你精心准备了许久的礼物" class="headerlink" title="真诚地向观众展示你精心准备了许久的礼物"></a>真诚地向观众展示你精心准备了许久的礼物</h2><p>在我做PPT的这些日子里，我总是怀着这样的心情去演讲，有的时候我做20页的幻灯片需要花费半天的时间(或许是效率问题😂)，不夸张地说这绝对是一件脑力活。正是因为准备了这么久所以才更希望展现在观众面前的时候按照自己的预期走，不要出一丁点差错，而你的观众就是其中最大的因素，所以必须对你的观众真诚，让他们眼前一亮，让他们的注意力只停留在你的演讲上，这便成功了一大半。</p><h2 id="请保持幽默"><a href="#请保持幽默" class="headerlink" title="请保持幽默"></a>请保持幽默</h2><p>当我们看乔布斯的演讲或者老罗的相声时总会感叹这些演讲的天才是如何做到保持幽默的，有的时候一个眼神、一个动作、一句话、一张图片、一个视频都可以调节现场的气氛，我们在这里不讲如何做到幽默因为我也没有资格讲，这种能力的培养因人而异，但是作为演讲者我们在平时可以有意识的培养自己这方面的能力。相信当掌握了这种能力之后你的演讲将会事半功倍！</p><h1 id="演讲后"><a href="#演讲后" class="headerlink" title="演讲后"></a>演讲后</h1><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>其实演讲跟平时的考试一样，考完试之后需要总结经验，但也不一样，因为当你把它当成一场考试的时候就失去它原本的乐趣，尽管享受其中的乐趣就行了。如果说总结，那么我们又需要从哪些方面进行总结呢？<br>首先询问你的观众，让他告诉你观看完整场演讲之后最真实的想法，观众的意见是最宝贵的，去其糟粕，选择其中对你有用的，改之；第二就是重新回顾你的PPT，同时边回忆你演讲时的场景，看看是否哪一页你讲得不好或者是幻灯片还可以做得更好。我就是经常这样的，现在来看以前做的幻灯片就觉得当初的自己太稚嫩了，有时甚至觉得一些动画一点道理都没有。所以我也一直认为<strong>好的幻灯片不是做出来的而是改出来的。</strong></p><h2 id="学习"><a href="#学习" class="headerlink" title="学习"></a>学习</h2><p>知己知彼才能百战不殆，上一点是自身的总结，那么这一点就是我们需要向别人来学习啦😉 我个人对科技界的发布会十分的热衷，观看这些发布会的时候不仅可以了解科技界的前沿，更重要的就是观看他们的演讲，而我关注比较多的是如下的发布会👇</p><blockquote><ul><li>一加手机的发布会</li><li>老罗的单口相声</li><li>Google的IO大会</li><li>苹果每年的新品发布会</li></ul></blockquote><p>从这些高质量的演讲中，我们能够学习到优秀的演讲者身上的优点，可以学习他们的幻灯片，当然这都是基于兴趣，如果没有兴趣那么看整场发布会是很枯燥的，如果这样我更建议去模仿他们的幻灯片，基本每次大型的发布会之后，网上都会流出发布会的幻灯片，我们都可以拿来借鉴。</p><h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>本篇文章主要是和大家一起分享我在制作PPT以及演讲中的一些心得，当然由于我个人能力有限，其中难免有一些讲的不是很透彻也有可能不够好，希望大家多多见谅。作为一名大三的学生我要进步和学习的还有很多，不过依旧希望这篇文章在一定程度上能够帮助到你，再次祝大家端午节快乐☺</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天是端午节假期的第一天，在这里祝大家假期快乐，不过像我这种渣渣就不休息了，撸起袖子继续学习啦😜 &lt;a href=&quot;http://wensibo.top/2017/05/15/GankClient/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;上篇文章&lt;/a&gt;已经向大家承诺会写一些技术以外的文章，所以这次我也履行诺言跟大家一起来分享一下我在最近的演讲过程的一些心得，当然这里最主要的还是讲如何用PPT(因为我穷得只能吃土所以就无法为大家讲解Keynote啦，但是大体的思想是差不多的)为你的演讲增色，毕竟作为程序员，在工作的时候不仅仅是只面对代码，有的时候如果具备一定的演讲才能，那么你的老板同事对你也会刮目相看。这篇文章纯粹只是我个人的经验总结，请大神轻喷😂&lt;br&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2017-5-28speaker/jobs.jpg&quot; alt=&quot;&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;&lt;/div&gt;
            &lt;/figure&gt;&lt;br&gt;
    
    </summary>
    
      <category term="演讲" scheme="http://www.wensibo.top/categories/%E6%BC%94%E8%AE%B2/"/>
    
    
      <category term="PPT" scheme="http://www.wensibo.top/tags/PPT/"/>
    
      <category term="演讲" scheme="http://www.wensibo.top/tags/%E6%BC%94%E8%AE%B2/"/>
    
  </entry>
  
  <entry>
    <title>又是一个MVP+RxJava+Retrofit的干货集中营</title>
    <link href="http://www.wensibo.top/2017/05/15/GankClient/"/>
    <id>http://www.wensibo.top/2017/05/15/GankClient/</id>
    <published>2017-05-15T14:51:25.000Z</published>
    <updated>2018-10-21T08:26:20.664Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>今天想要与大家一起分享的是近一个星期内开发的一个新app——<strong>干货集中营客户端</strong>，其实网上已经有许多类似的项目，代码家也在他的干货集中营中推荐了几款优秀的作品，我也借鉴了其中的一些，自己开发出一款干货集中营客户端，目前项目已经发布到<a href="https://github.com/Wensibob/GankClient" target="_blank" rel="noopener">github</a>，如果你想了解整个工程的具体内容，那么你可以fork或者clone，如果你觉得我做得还可以，那么请你赐给我一个star呗！你的支持会是我努力的动力。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-15GankClient/io.png" alt="Google_I/O_2017" title="">                </div>                <div class="image-caption">Google_I/O_2017</div>            </figure><br><a id="more"></a> </p><script>console.error("Error: [hexo-tag-aplayer] Unrecognized tag argument(2): autoplay=ture");</script><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>慢慢的已经养成了每篇文章都会来一个前言，也慢慢地将写文章潜移默化的转变成写故事，所以每个故事都会有前言，也都会有结果，这次也不例外。<br>距离上篇引起<a href="http://wensibo.top/2017/04/13/2017Tencent_review/" target="_blank" rel="noopener">热烈反响的文章</a>发布已经过去一个月了，承蒙广大网友对我的支持以及建议，当然作为一个90后，我也很虚心的接受来自各方的吐槽，毕竟我并不优秀，只是一直在优秀的路上努力的奔跑着。下面我想跟大家一起分享一下这一个月里我做了些什么事，以及接下来一段时间的计划和打算。</p><h3 id="Have-Done-List"><a href="#Have-Done-List" class="headerlink" title="Have Done List"></a>Have Done List</h3><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">持续<strong>22</strong>天在github上出现，看看下面这棵贡献树<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/contribution.png" alt="contributions"><br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">博客浏览量突破<strong>35000</strong><br><img src="http://wensibo.top/file/img/2017-5-15GankClient/blog.png" alt="blog"><br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">看了<strong>1</strong>本好书——《网络是怎样连接的》<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">研究Retrofit与RxJava的源码（待我葡萄成熟时再把文章放出来吧，现在太嫩了，不好意思让大家看到）<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt=""><strong>2</strong>次担任演讲的主讲人(之后会写一篇文章讲述我的演讲技巧)<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">看了锤子科技新品发布会(我是罗粉但也是加油，心疼老罗一秒)<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">努力寻找实习(投了许多简历，但是无一幸免:sob: ,有同学可以推荐一波吗？没有的话我待会再问问)<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt=""><strong>1</strong>个全新的干货集中营客户端app(也是写这篇文章的缘由)        </p><h3 id="Todo-List"><a href="#Todo-List" class="headerlink" title="Todo List"></a>Todo List</h3><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">Google I/O 2017开发者大会<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">xposed插件开发(这个闪念是有一天夜里惊醒过来的想法)<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">继续写好的文章与大家一起分享，不仅仅是技术方面的，还有许多我觉得有用的文章，都会为大家奉上，感谢老铁们的支持<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">继续看书<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">继续找实习(呜呜呜<del>~~(&gt;_&lt;)</del>~~,体会到工作难找了，尤其是互联网寒冬)        </p><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/car.jpg" alt="老铁别废话了"><br>好啦，上面讲了一大堆废话终于可以进入正题了，我也很基动，开车咯！</p><h2 id="项目介绍"><a href="#项目介绍" class="headerlink" title="项目介绍"></a>项目介绍</h2><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/logo.png" alt="logo"><br>GankClient(又称干货集中营客户端)是一个全新的干货集中营客户端，它获取的是来自<a href="http://gank.io/api" target="_blank" rel="noopener">干货集中营API</a>的数据，利用全新的Material Design的设计语言展示数据内容。整个项目采用MVP的设计架构，并且大量使用RxJava2，网络框架使用的是Retrofit。我用下面几个装逼的句子来形容一下这个APP：    </p><ul><li>更快速的加载速度</li><li>更流畅的页面体验</li><li>更有趣的刷新效果</li><li>更精美的网页浏览</li><li>更美观的视觉享受    </li></ul><p>这个只是我的个人感受，大神请轻喷:joy: ，不过还是希望大家能够喜欢<a href="https://github.com/Wensibob/GankClient" target="_blank" rel="noopener">这个项目</a>，并且积极的向我pull request以及反馈bug，希望大家多多支持。如果可以的话给个star咯。    </p><h2 id="预览"><a href="#预览" class="headerlink" title="预览"></a>预览</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-15GankClient/no_picture.jpg" alt="谁说不给图的" title="">                </div>                <div class="image-caption">谁说不给图的</div>            </figure>        <h3 id="全部效果图来一发"><a href="#全部效果图来一发" class="headerlink" title="全部效果图来一发"></a>全部效果图来一发</h3><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/1.png" alt="screenshot">    <img src="http://wensibo.top/file/img/2017-5-15GankClient/2.png" alt="screenshot">    <img src="http://wensibo.top/file/img/2017-5-15GankClient/3.png" alt="screenshot">    <img src="http://wensibo.top/file/img/2017-5-15GankClient/4.png" alt="screenshot">    <img src="http://wensibo.top/file/img/2017-5-15GankClient/5.png" alt="screenshot">    <img src="http://wensibo.top/file/img/2017-5-15GankClient/6.png" alt="screenshot"></p><h3 id="没有gif图都不好意思说话了"><a href="#没有gif图都不好意思说话了" class="headerlink" title="没有gif图都不好意思说话了"></a>没有gif图都不好意思说话了</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-5-15GankClient/gank.gif" alt="gif" title="">                </div>                <div class="image-caption">gif</div>            </figure>    <h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>如果你想测试这个项目，那么请手动<a href="https://github.com/Wensibob/GankClient" target="_blank" rel="noopener">clone</a>，如果你想尝试一下APP，那么你可以使劲<a href="http://fir.im/1110" target="_blank" rel="noopener">戳这里</a> ,或者扫下面的二维码。后期我会发布到应用市场，希望大家可以多多支持！<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/QRCode.png" alt="QRCode">    </p><h2 id="功能-amp-特色"><a href="#功能-amp-特色" class="headerlink" title="功能&amp;特色"></a>功能&amp;特色</h2><p> <strong>√表示已经实现的功能，没有√的是还没实现或者需要完善的功能。</strong>             </p><p><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">实时获取服务器的最新数据，采用CardView的布局以更好的展现数据。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">刷新效果很好玩，真的很精致，感谢<a href="https://github.com/Yalantis/Phoenix" target="_blank" rel="noopener">Phoenix</a>。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">如果你装有Chrome浏览器，并将其设置为默认浏览器，那么你的网页浏览效果就会十分酷炫。感谢<a href="https://github.com/GoogleChrome/custom-tabs-client" target="_blank" rel="noopener">Custom-Tabs-Client</a> ! 墙裂推荐Chrome，如果你没有安装Chrome，那也没关系，那就用传统的WebView吧！<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">更加统一的过渡动画，相信你会爱上它。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">保存、分享图片与链接，也可以直接使用浏览器打开链接。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">更好看的Material Design设计风格。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/check.png" alt="">妹子很漂亮哟！<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">优化webview的视频播放。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">增加使用系统浏览器的选项。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">修复在主界面中加载数据到2016/4/20左右时数据显示错乱的问题。<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/not_check.png" alt="">增强分享功能。              </p><h2 id="分解"><a href="#分解" class="headerlink" title="分解"></a>分解</h2><p>终于到了这个纯干货步骤了，别说话先看东西！             </p><h3 id="如何使用MVP的设计架构"><a href="#如何使用MVP的设计架构" class="headerlink" title="如何使用MVP的设计架构"></a>如何使用MVP的设计架构</h3><p>我先展示以下这个项目的结构图，让大家心里有个底             </p><ul><li>http<ul><li>GankRetrofit.java</li><li>GankRetrofitClient.java</li></ul></li><li>mvp<ul><li>model<ul><li>BaseModel.java</li></ul></li><li>presenter<ul><li>BasePresenter.java</li></ul></li><li>view<ul><li>IBaseView.java</li></ul></li></ul></li><li>ui<ul><li>activity</li><li>adapter</li><li>base</li><li>chromeviews</li><li>fragment</li><li>widget   </li></ul></li></ul><p>可以看到，整个项目的结构还算比较清晰，整体的架构都是在mvp目录中定下来的，mvp架构分三层，<strong>model模型层</strong>，用于定义一些数据的类型；<strong>presenter呈现者层</strong>，用于处理view层的逻辑；<strong>view显示层</strong>，这里定义的都是接口，真正实现他们的是activity，这些activity只要实现相应的接口，就能够自己复写其中的方法。<br>当然我这么说你肯定还是一脸懵逼，而且我还必须跟你说，我这个不算最纯粹的MVP模式，最纯粹的写法应该都是面向接口编程的，就是在model，presenter，以及view下都是定义接口，而到了具体的运用场景，就定义出具体的类去实现这些接口。当然了，为了让大家能够更清楚MVP是怎么实现的，下面我会用代码的形式为大家展示，例如我要编写MainActivity的代码，首先已经知道在这个Activity中需要做如下这些事：</p><ul><li>初始化组件</li><li>加载Fragment</li><li>FloatingActionButton的点击事件——去到DailyActivity</li><li>drawer menu的点击事件——去到AboutActivity</li></ul><p>为了让Activity专注于界面的工作，而不用去考虑逻辑处理的操作，这个就是所谓的解耦，那么我们可以将逻辑的操作放到presenter中去，而与界面有关的操作就放在view下，于是我们就可以这样写(为了便于大家理解，我将项目中的代码做了适当的修改，如果你想了解更加具体的逻辑，可以仔细看看代码)：    </p><ul><li><strong>IBaseView</strong>    </li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IBaseView</span> </span>&#123;</span><br><span class="line">        <span class="comment">//界面初始化操作</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><strong>BasePresenter&amp;MainPresenter</strong>    </li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 将presenter的公共操作集成为BasePresenter</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BasePresenter</span> &lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">IBaseView</span>&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">protected</span> Disposable disposable;</span><br><span class="line">        <span class="keyword">protected</span> Context context;</span><br><span class="line">        <span class="keyword">protected</span> T iView;      <span class="comment">//获取到view的对象</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">BasePresenter</span><span class="params">(Context context, T iView)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">this</span>.context = context;</span><br><span class="line">                <span class="keyword">this</span>.iView = iView;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                iView.init();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">release</span><span class="params">()</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">initPresenter</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 每一个界面都可以编写自己的Presenter，并指定View的泛型</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainPresenter</span> <span class="keyword">extends</span> <span class="title">BasePresenter</span>&lt;<span class="title">IBaseView</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">MainPresenter</span><span class="params">(Context context, IBaseView iView)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>(context, iView);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">release</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">toDailyActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                Intent intent = <span class="keyword">new</span> Intent(context, DailyActivity.class);</span><br><span class="line">                context.startActivity(intent);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">toAboutActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                Intent intent = <span class="keyword">new</span> Intent(context, AboutActivity.class);</span><br><span class="line">                context.startActivity(intent);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">toSettingActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                Intent intent = <span class="keyword">new</span> Intent(context, SettingActivity.class);</span><br><span class="line">                context.startActivity(intent);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><ul><li><strong>MainActivity</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> &lt;<span class="title">MainPresenter</span>&gt; <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> <span class="keyword">implements</span> <span class="title">IBaseView</span></span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@OnClick</span>(R.id.fab_main)</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">fabClick</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                presenter.toDailyActivity();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(@Nullable Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">                setContentView(R.layout.activity_main);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initPresenter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                presenter = <span class="keyword">new</span> MainPresenter(<span class="keyword">this</span>, <span class="keyword">this</span>);</span><br><span class="line">                presenter.init();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="comment">//所有的初始化操作</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onDestroy();</span><br><span class="line">                presenter.release();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大概就是这样一个套路，讲真我这样讲你肯定迷糊，我当初在学这个的时候也是十分凌乱，后来才发现只有自己动手亲自敲代码才能了解整个思路，另外大家在学习MVP的过程中应该擅于画图，不管是在纸上画还是使用思维导图都行，这样可以让你更加宏观的了解整个调用的顺序以及各个类之间的关系，这绝不是你看了一篇文章就能懂的。</p><h3 id="你真的用好RxJava了吗"><a href="#你真的用好RxJava了吗" class="headerlink" title="你真的用好RxJava了吗"></a>你真的用好RxJava了吗</h3><p>当我问这个问题的时候，其实我想说的是RxJava的用处很广，我们想当然的认为只要RxJava与Retrofit相配合就算是完成任务了，但实际上只要涉及到耗时操作、线程切换：网络请求、图片解析、数据库读取等等需要用多个线程一起完成的工作时，你都可以用到RxJava，并且如果你还一直用RxJava1的话，那么你也OUT啦，当你认识RxJava2.x的时候你会发现他更强大了。关于RxJava，我后续会写相关的文章与大家一起分享，这里我举项目中遇到的一个例子，看看RxJava的用武之地是多么的广：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 将图片保存在本地</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">openWebView</span><span class="params">(<span class="keyword">final</span> Gank gank)</span> </span>&#123;</span><br><span class="line">    disposable=Observable.create(<span class="keyword">new</span> ObservableOnSubscribe() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">subscribe</span><span class="params">(@NonNull ObservableEmitter e)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">                    e.onNext(android.R.drawable.ic_menu_share);</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;).subscribeOn(Schedulers.newThread())<span class="comment">//开启一个新线程来进行耗时操作</span></span><br><span class="line">            .map(<span class="keyword">new</span> Function&lt;Integer, Bitmap&gt;() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="function"><span class="keyword">public</span> Bitmap <span class="title">apply</span><span class="params">(@NonNull Integer integer)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">                            <span class="comment">//将资源id解析为bitmap是一个耗时操作</span></span><br><span class="line">                            <span class="keyword">return</span> BitmapFactory.decodeResource(activity.getResources(), integer);</span><br><span class="line">                    &#125;</span><br><span class="line">            &#125;).observeOn(AndroidSchedulers.mainThread())<span class="comment">//回到主线程操作bitmap</span></span><br><span class="line">            .subscribe(<span class="keyword">new</span> Consumer&lt;Bitmap&gt;() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(@NonNull Bitmap bitmap)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">                            <span class="comment">//拿到bitmap之后做的界面更新</span></span><br><span class="line">                    &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">&#125;</span><br><span class="line">        </span><br><span class="line"><span class="comment">//释放资源</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">release</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (disposable != <span class="keyword">null</span>&amp;&amp;!disposable.isDisposed()) &#123;</span><br><span class="line">                        disposable.dispose();</span><br><span class="line">                &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><h3 id="爽快的Retrofit"><a href="#爽快的Retrofit" class="headerlink" title="爽快的Retrofit"></a>爽快的Retrofit</h3><p>还记得之前我写过Volley的一系列文章，虽然觉得这个开源框架已经不错了，但毕竟长江后浪推前浪，在Retrofit的面前，Volley简直就是小巫见大巫，看看我们应该如何在项目中使用：</p><ul><li>1、新建一个接口，用注解的形式在里面定义网络请求的方法：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GankRetrofit</span> </span>&#123;</span><br><span class="line">        <span class="comment">//获取MainActivity界面的数据，具体的请求路径应该详细看官方的API说明，</span></span><br><span class="line">        <span class="comment">// Retrofit与RxJava配合之后才能显示出更强的威力</span></span><br><span class="line">        <span class="meta">@GET</span>(<span class="string">"data/&#123;type&#125;/40/&#123;page&#125;"</span>)</span><br><span class="line">        <span class="function">Observable&lt;MainData&gt; <span class="title">getMainData</span><span class="params">(@Path(<span class="string">"type"</span>)</span> String type, @<span class="title">Path</span><span class="params">(<span class="string">"page"</span>)</span> <span class="keyword">int</span> page)</span>;</span><br><span class="line">&#125;</span><br><span class="line">```    </span><br><span class="line"></span><br><span class="line">* <span class="number">2</span>、获得实例：</span><br><span class="line"></span><br><span class="line">```java</span><br><span class="line">Gson date_gson = <span class="keyword">new</span> GsonBuilder().setDateFormat(<span class="string">"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"</span>).create();</span><br><span class="line">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">        .baseUrl(<span class="string">"http://gank.io/api/"</span>)         <span class="comment">//指定服务器地址</span></span><br><span class="line">        .addConverterFactory(GsonConverterFactory.create(date_gson))    <span class="comment">//添加一个gson的解析器</span></span><br><span class="line">        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())          <span class="comment">// 如果想使用RxJava那就需要添加这个适配器  </span></span><br><span class="line">        .build();</span><br><span class="line">GankRetrofit gankRetrofit;</span><br><span class="line">gankRetrofit = retrofit.create(GankRetrofit.class);            <span class="comment">//获取GankRetrofit的对象</span></span><br></pre></td></tr></table></figure><ul><li>3、获取数据：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//返回的Observable&lt;MainData&gt;对象就可以使用RxJava进行解析了</span></span><br><span class="line">gankRetrofit.getMainData(<span class="string">"Android"</span>,<span class="number">40</span>);</span><br></pre></td></tr></table></figure><h3 id="更好的网页浏览体验——Custom-Tabs-Client"><a href="#更好的网页浏览体验——Custom-Tabs-Client" class="headerlink" title="更好的网页浏览体验——Custom-Tabs-Client"></a>更好的网页浏览体验——<a href="https://github.com/GoogleChrome/custom-tabs-client" target="_blank" rel="noopener">Custom-Tabs-Client</a></h3><p>如果你使用过Chrome浏览器，那么你应该会喜欢上它，当我们在使用webview开发的时候，会发现webview存在许多的限制，并且开发者对webview不能完全控制，于是Chrome团队为了解决开发者的这些问题，就设计出这个开源库，只要用户手机上安装Chrome并且设置为默认浏览器，那么打开网页链接的时候就会看到如下的画面：<br><img src="http://wensibo.top/file/img/2017-5-15GankClient/custom.png" alt="custom-tabs-client"><br>是的！如此酷炫的画面是用到了Chrome浏览器的内核，并且读取Chrome的cookie，例如如果你在Chrome已经登录过github了，那么通过这个库打开的github链接同样也显示你已经登录了github。当然除了页面效果十分好之外，我们还可以自定义许多东西，例如<strong>过渡动画</strong>、<strong>ToolBar上方的ActionButton</strong>等等，我的项目中也已经实现了这两个功能。接下来我带大家一起看看最简单的实例应该怎么编写：</p><pre><code class="java">CustomTabsIntent.Builder customTabsIntent; customTabsIntent = <span class="keyword">new</span> CustomTabsIntent.Builder();     <span class="comment">//获取到CustomTabsIntent实例</span> <span class="comment">//一系列的设置</span>customTabsIntent.setToolbarColor(activity.getResources().getColor(R.color.colorPrimaryDark));   <span class="comment">//设置ToolBar的颜色</span>customTabsIntent.setShowTitle(<span class="keyword">true</span>);    <span class="comment">//设置是否显示网页的标题</span>customTabsIntent.setStartAnimations(activity, R.anim.slide_in_right, R.anim.slide_out_left);    <span class="comment">//设置进入的动画</span>customTabsIntent.setExitAnimations(activity, R.anim.slide_in_left,R.anim.slide_out_right);      <span class="comment">//设置退出的动画</span><span class="comment">//开启网页</span>CustomTabActivityHelper.openCustomTab(    activity,       <span class="comment">//当前的activity</span>    customTabsIntent.build(),    view,    gank,   <span class="comment">//gank带有要打开的网页的url</span>    <span class="keyword">new</span> CustomFallback() { <span class="comment">//如果用户没有安装Chrome并将其设置为默认浏览器的话，应该做这样的处理</span>            <span class="meta">@Override</span>            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">openUri</span><span class="params">(Activity activity, View view,Gank gank)</span> </span>{                    Log.d(<span class="string">"Gank"</span>, gank.toString());                    <span class="keyword">super</span>.openUri(activity, view,gank);            }    });</code></pre><hr><p><strong>以上就是想要跟大家一起分享的关于这个项目中一些重要的点，可能有些地方讲得并不那么清楚，但我觉得只有实践才能出真知，所以老哥还是乖乖打开AS撸撸代码啦！</strong></p><h2 id="版本控制"><a href="#版本控制" class="headerlink" title="版本控制"></a>版本控制</h2><p>目前app的版本为V1.0.0，我会用下面的时间表记录版本的迭代，如果有更新的版本，我会及时更新这个表格。</p><table><thead><tr><th style="text-align:center">Version</th><th style="text-align:center">Date</th></tr></thead><tbody><tr><td style="text-align:center"><a href="https://github.com/Wensibob/GankClient/releases/tag/V1.0.0" target="_blank" rel="noopener">V1.0.0</a></td><td style="text-align:center">2017/5/14</td></tr></tbody></table><h2 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a>致谢</h2><p>作为一名Android开发者，我们都应该具有像罗老师一样感激开源世界的情怀，而我们现在能做的就是同样为开源世界做贡献，无论是写文章亦或是写程序，记得都要时刻保持一颗感恩的心。非常感谢代码家以及他的干活集中营，也非常感谢许多前辈们做过的干活集中营app。</p><ul><li><a href="https://daimajia.com/" target="_blank" rel="noopener">代码家</a></li><li><a href="http://gank.io/" target="_blank" rel="noopener">干货集中营</a></li><li><a href="https://github.com/Panl/Gank.io" target="_blank" rel="noopener">Gank.lu</a></li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>最近一个月，感觉自己身体被掏空，吐槽学校对我们专业的课程安排如此不合理，每天上那些自己不感兴趣的课程确实很无趣，但是生活再难也要感激它，只有通过自己的双手去努力，将来的你才会为现在的自己喝彩骄傲。下篇文章再见！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天想要与大家一起分享的是近一个星期内开发的一个新app——&lt;strong&gt;干货集中营客户端&lt;/strong&gt;，其实网上已经有许多类似的项目，代码家也在他的干货集中营中推荐了几款优秀的作品，我也借鉴了其中的一些，自己开发出一款干货集中营客户端，目前项目已经发布到&lt;a href=&quot;https://github.com/Wensibob/GankClient&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github&lt;/a&gt;，如果你想了解整个工程的具体内容，那么你可以fork或者clone，如果你觉得我做得还可以，那么请你赐给我一个star呗！你的支持会是我努力的动力。&lt;br&gt;&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2017-5-15GankClient/io.png&quot; alt=&quot;Google_I/O_2017&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;Google_I/O_2017&lt;/div&gt;
            &lt;/figure&gt;&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="MVP" scheme="http://www.wensibo.top/tags/MVP/"/>
    
      <category term="RxJava" scheme="http://www.wensibo.top/tags/RxJava/"/>
    
      <category term="Retrofit" scheme="http://www.wensibo.top/tags/Retrofit/"/>
    
  </entry>
  
  <entry>
    <title>给21岁的自己</title>
    <link href="http://www.wensibo.top/2017/04/16/birthday/"/>
    <id>http://www.wensibo.top/2017/04/16/birthday/</id>
    <published>2017-04-15T16:00:00.000Z</published>
    <updated>2018-10-21T14:53:22.524Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>没有那么多的前言啦！点进去看图片吧！</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.top/file/img/2017-4-15birthday/birthday.png" alt="诗和远方" title="">                </div>                <div class="image-caption">诗和远方</div>            </figure><a id="more"></a>            <div id="aplayer-teSQoMBi" class="aplayer aplayer-tag-marker" style="margin-bottom: 20px;">            <pre class="aplayer-lrc-content"></pre>        </div>        <script>          var ap = new APlayer({            element: document.getElementById("aplayer-teSQoMBi"),            narrow: false,            autoplay: true,            showlrc: false,            music: {              title: "安静",              author: "周杰伦",              url: "http://wensibo.top/file/img/2017-4-15birthday//quite_jay.mp3",              pic: "http://wensibo.top/file/img/2017-4-15birthday/quite_jay.png",              lrc: ""            }          });          window.aplayers || (window.aplayers = []);          window.aplayers.push(ap);        </script>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;没有那么多的前言啦！点进去看图片吧！&lt;/p&gt;
&lt;figure class=&quot;image-bubble&quot;&gt;
                &lt;div class=&quot;img-lightbox&quot;&gt;
                    &lt;div class=&quot;overlay&quot;&gt;&lt;/div&gt;
                    &lt;img src=&quot;http://wensibo.top/file/img/2017-4-15birthday/birthday.png&quot; alt=&quot;诗和远方&quot; title=&quot;&quot;&gt;
                &lt;/div&gt;
                &lt;div class=&quot;image-caption&quot;&gt;诗和远方&lt;/div&gt;
            &lt;/figure&gt;
    
    </summary>
    
      <category term="生日" scheme="http://www.wensibo.top/categories/%E7%94%9F%E6%97%A5/"/>
    
    
  </entry>
  
  <entry>
    <title>2017腾讯实习生Android客户端开发面试总结</title>
    <link href="http://www.wensibo.top/2017/04/13/2017Tencent_review/"/>
    <id>http://www.wensibo.top/2017/04/13/2017Tencent_review/</id>
    <published>2017-04-13T15:49:25.000Z</published>
    <updated>2018-10-21T08:26:20.496Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>本篇文章已授权微信公众号 guolin_blog （郭霖）独家发布</strong></p><p><strong>或许你已经看过这篇文章，但我想告诉大家的是其实我并没有大家说的那么厉害，本人学到的还只是皮毛中的皮毛，相比起很多大神我自己只有负责仰望的权利，所以如果可以的话请在本篇文章的最后留下你对我的建议，记住是建议！</strong></p><p>先做个自我介绍，本人大三狗一枚，就读的是广州一个普通的一本大学(非985、211)，专业是比较尴尬的电子商务(非计算机学院，连C的课程都没有就只有Java)，但是一切的尴尬并没有阻挡我对Android开发的热爱，自学一年多了，基础以及开发的技术也掌握的相对成熟了。在即将要变身大四狗的关键节点看到了腾讯爸爸暑期实习生的招聘，便刻不容缓地参加笔试，很开心顺利收到面试通知，可惜最终止步二面，以下是我此次面试的整个过程，希望对大家有所帮助。</p><script>console.error("Error: [hexo-tag-aplayer] Unrecognized tag argument(2): autoplay=ture");</script><a id="more"></a><h1 id="伏笔"><a href="#伏笔" class="headerlink" title="伏笔"></a>伏笔</h1><p>一面的时间是11号下午2点钟，因为对酒店的位置不熟悉，所以提前了两个小时出发，下公车过马路的时候下起了小小雨，所以撑起了雨伞，这时站在我前方的一个中年女士回头看着我的雨伞，我似乎看到了她眼神中对雨伞的渴望，所以走上前去给她撑伞，她愣了一下说她不是想要撑伞，只是想到今天自己忘记带雨伞了不知该怎么办。送她走过马路的过程中，我告知她要去参加腾讯的面试，她说面试所在酒店的老板是她朋友(蜜汁剧情😂)，后来告别的时候她说祝我好运并叫我记得跟面试官说我今天运气很好哟(尴尬连 😳)。其实面试结束之后觉得此事很蹊跷，但也被她说中我一面运气很好，所以也很感激这位陌生的中年女士吧！</p><h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-4-14Tencent/20170411_130751.jpg" alt="手持OP3拍一面" title="">                </div>                <div class="image-caption">手持OP3拍一面</div>            </figure><p>来一面的人很多，等候的会议厅里显得有点嘈杂，不过很意外的是来面试的学生中男女比例相当，更可以说女生稍稍比男生多一丢丢，看来程序媛也很努力啊！<br>2点过几分钟，系统发短信提醒我可以面试了，腾讯的高效可见一斑，以下是正文。<br><strong>我：</strong>敲门，问好，深深鞠躬并双手递上简历。一面的面试官有些许腼腆，偏瘦偏黑，坐得很直，第一感觉很Nice。<br><strong>面试官：</strong>你好，先做个自我介绍吧。<br><strong>我：</strong>将自己准备了许久的自我介绍顺利地讲了一遍，面试官很认真的听并逐点看我的简历。<br><strong>面试官：</strong>听完自我介绍之后，问道：你是自学Android还是学校里有相关的课程。<br><strong>我：</strong>苦逼的我当然是自学的啦，大一的时候学校教的是Java，老师很好，也是我在大学期间觉得唯一的一个好老师了。讲自己从一开始通过看郭霖的《第一行代码》入门，到中间走了许多弯路去看了一些补习机构的教程视频，再到后来自己总结出了一个学习路线图，并开始专注于看优秀的图书和勤打代码，并且做了一些比较拿得出手的项目和维护着一个<a href="http://wensibo.com.cn" target="_blank" rel="noopener">个人博客</a> 。<br><strong>面试官：</strong>频频点头，似乎很同意我的学习历程并且对我目前的状态表示满意。接着问，知道Android中的ANR吗？解释一下。<br><strong>我：</strong>知道，ANR就是Application Not Responding ，即应用程序未响应，之所以会造成这种异常是因为Android是在主线程即UI线程中更新界面的，但是如果在UI线程中进行过多的耗时操作就会堵塞主线程从而造成ANR，具体造成ANR的原因有三个：Activity耗时操作超过5s，Broadcast Receiver 超过10s，Service超过15s。<br><strong>面试官：</strong>似乎很满意，接着问道，那Android中是如何避免ANR的？<br><strong>我：</strong>这些问题因为都是很基础的，并且面试前复习过了，所以对答如流。我说我举一种方法吧，例如AsyncTask，具体介绍了他的doInBackground和updateProgress以及postExecute三个方法的使用以及参数的类型转换，还分析了AsyncTask的缺点，就是它所维护的线程池大小为128，同一时刻只能有5个工作线程和一个缓存线程，如果耗时操作工作量巨大就会导致线程池大小不够用，这就是它的缺点，另外我还介绍了它的解决方式，就是由一个控制线程来处理AsyncTask的调用，判断线程池是否已经满了，如果满的话就停止处理。<br><strong>面试官：</strong>一番回答下来面试官从看着简历到一直看着我并且频频点头，似乎对我的回答十分满意，我也暗自窃喜。接着他就问除了AsyncTask之外，有用过Handler吗？具体解释一下它的实现过程。<br><strong>我：</strong>这种问题可以说是每次面试必问的，并且自己也研究过源码，对Looper的实现机理十分熟悉，所以又是讲了一大堆。<br><strong>面试官：</strong>对我的回答十分满意，并问我是不是经常看源码？<br><strong>我：</strong>还好，毕竟也不是什么源码都看，如果被问到没看过的就尴尬了，还是谦虚点。<br><strong>面试官：</strong>Android应用程序之间是通过哪些方式共享数据的？<br><strong>我：</strong>File，Sqlite，Content Provider，Service，BroadCast Receiver，Intent，同个Application内部的话还可以通过静态变量共享数据。<br><strong>面试官：</strong>点点头，这个面试官很喜欢点头。那能不能说说Content Provider的生命周期？<br><strong>我：</strong>感觉有点懵逼，四大组件中似乎很少接触到Content Provider的生命周期，自己也不是很确定，就说生命周期不是很懂，但是我可以说一下它的实现机理，就说了Content Provider，Content Resolver之间的使用规则，具体到程序间的URI。后来回学校之后查了一下，发现Content Provider并没有生命周期这一说法，不知道面试官是问错了还是故意挖坑给我跳的，好在没往里面跳。<br><strong>面试官：</strong>开始问到简历上面的项目，先问了Volley的实现机理。<br><strong>我：</strong>因为之前写过四篇文章来介绍Volley，所以Volley的实现机理很清楚，便给他详细的介绍了起来，具体的可以看我的<a href="http://wensibo.com.cn/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E5%9B%9B%EF%BC%89/" target="_blank" rel="noopener">这篇Volley源码分析</a> 。<br><strong>面试官：</strong>你刚才对Volley的实现机理讲的十分详细，调理也很清晰，很不错，那你知道为什么线程池里面是有5个线程在循环呢？为什么不是6个7个或者8个呢？<br><strong>我：</strong>哎呀，这个问题倒是没有细细去想，但是当初也很奇怪为什么会定5作为全局常量呢？会不会因为5是开发者的幸运数字呢(黑人问号)，后来想了一下对面试官说，可能是因为Volley面向的网络请求是请求量比较小但是请求比较频繁的操作，所以5应该是刚好能够处理得来的，又或许5是一个经过大量的数据实验之后得到的一个相对符合的数量吧！<br><strong>面试官：</strong>笑了笑，其实我也不是很清楚。(尴尬 😧 ，面试官好像想逗我一下)。接着问：说道网络请求方面，你能不能说说像QQ这种IM的通讯机理是怎么样的？<br><strong>我：</strong>有点紧张 😰 ，因为复习时间比较短，所以还没仔细看网络方面的知识，所以就大概说了一下4层结构还有3次握手。<br><strong>面试官：</strong>似乎觉得还可以，继续点头。接着问：Volley网络请求了哪些数据类型？<br><strong>我：</strong>String字符串，图片，XML，JSON。<br><strong>面试官：</strong>服务器是自己搭的吗？<br><strong>我：</strong>尴尬了一下，不，不是自己搭的，是用到了一些天气和全国城市的接口数据，因为当时在做调试，所以没有心思去搭建一个服务器，不过在大二的时候学过WEB开发，对Tomcat比较熟悉，并且简历上的第二个项目就是我当初做的Blog项目，所以对服务器的搭建还是有一些心得的，接着就跟他说了一些搭建的步骤。<br><strong>面试官：</strong>对我的回答很满意，毕竟学Android的如果能够对服务器端有比较深的认识的话在做数据测试的时候是可以省去团队的很多开支的。接着问：说说XML、JSON、GSON有什么样的联系？<br><strong>我：</strong>这个很简单了，有接触过的应该都知道，XML全称叫做可扩展标记语言，它的结构相对简单，可读性强，但是对于一些比较复杂的数据结构就很难存储，JSON的话刚好弥补了XML的这些缺点，至于GSON的话，是因为Google的一个开源库而得名的，这个开源库可以很方便地将JSON数组转换为对象，这在开发中简化了将JSON的字段转换为属性的步骤。<br><strong>面试官：</strong>看着简历问了我的便签项目是如何实现的？<br><strong>我：</strong>因为我做过的很多项目都是会写一些文章来介绍里面实现的机理，如果你对此感兴趣，可以看我这篇<a href="http://wensibo.com.cn/2017/03/08/%E5%86%99%E4%B8%80%E4%B8%AA%E5%B0%8F%E4%BE%BF%E7%AD%BE/" target="_blank" rel="noopener">写一个小便签</a> 。<br><strong>面试官：</strong>数据库的操作用到的是什么类型的数据库？<br><strong>我：</strong>用到的普通的Sqlite。其实我还可以多扩展点的，例如用过GreenDAO框架，所以还可以说说用了GreenDAO与用传统的Sqlite的操作的区别。<br><strong>面试官：</strong>说说你用Github做了些什么？<br><strong>我：</strong>有点不太清楚面试官问这个问题的意思 😮 ，就说经常push项目，订阅了代码家的邮箱推送，每天会推送一些Github的好项目，所以会fork一些优秀的项目，并且在Indigo项目中积极地发issue。<br><strong>面试官：</strong>点点头，似乎觉得还可以。学过C/C++吗？<br><strong>我：</strong>很尴尬，作为一个<code>电子商务</code>专业的学生很怕被问到这个问题，不过还是如实地向他说明情况，没学过C/C++，但是学过JAVA，数据结构、算法、计算机网络，并且我在自学操作系统。<br><strong>面试官：</strong>显然很惊讶没有学过C，但是考虑到是Android开发，所以也就没多为难我，并且了解到我自学操作系统露出了笑容，接着让我说一下算法。<br><strong>我：</strong>咳，算法准备的不是很充分，所以鸡贼的向他讲了最简单的冒泡排序，还没讲完他就频频点头。舒了一口气。<br><strong>面试官：</strong>经常写博客对吧！说说自己写得最好的一篇博客吧。<br><strong>我：</strong>很欣喜，向他讲起了写Volley源码的那四篇文章，他也听得津津有味，感觉自己坚持做的一些事情终于得到别人的认可了，很开心 😊 。<br><strong>面试官：</strong>有没有觉得自己做得比较自豪的事情但是我还没有问到的？<br><strong>我：</strong>写博客啊！向他说道一开始是用的博客园的第三方博客，但是后来很想有自己可以DIY的个人博客，并且很喜欢<code>Material Design</code>所以就花了很多时间来建站，从博客程序的安装，到域名购买、服务器维护、网站推广，自己都用了很多的时间和心思，还说了自己很喜欢MarkDown，真心觉得Word可以仍掉了。他边听边笑。<br><strong>面试官：</strong>听到我写博客的这个过程很是满意，对我露出了笑容。看了看时间，说道：面试快结束了，有什么问题想要问我的吗？<br><strong>我：</strong>面试前就准备好问题了，所以像个小孩子一样问他我可以问您两个问题吗？右手比起了剪刀手。<br><strong>面试官：</strong>当然可以。<br><strong>我：</strong>第一个问题是让他对我的此次面试做一个评价，第二个的话是想问一下面试官的邮箱，以便在往后的学习工作中请教。<br><strong>面试官：</strong>“HR团队有规定，面试官不能与学生们私下交流，所以这个请求我可能无法实现，如果接下来的面试有机会的话我再跟你联系，希望你见谅。至于第一个问题的话，我觉得你挺优秀的，作为一个不是计算机专业的学生能够在大一的时候就清楚自己未来在Android开发的方向，并且为此而努力着，你说到你写博客的时候我也很喜欢。” 他讲起他与他的同事们在工作中也累计了很多的经验与知识，但是分享的群体也就局限于他们团队，他说像我这样把我学到的知识分享给更多的人，这种也是开源精神的一种体现，希望我继续葆有这样的热情，另外就是我对源码的研究十分的仔细，回答问题的时候逻辑十分清晰，看来对源码的理解是十分的到位的，希望我能够继续保持这个习惯，这样我才能够学到更多的知识。</p><p><strong>不出意料，当天晚上接到了二面的通知，第二天早上10点20的面试，似乎觉得自己运气挺不错的，也好像应了那位女士说的。</strong></p><h1 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h1><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://wensibo.com.cn/file/img/2017-4-14Tencent/20170412_094221.jpg" alt="手持OP3拍二面" title="">                </div>                <div class="image-caption">手持OP3拍二面</div>            </figure><p>二面前天晚上，因为想到二面可能是总监面，所以抓紧时间复习数据结构、算法还有网络方面的知识，因为就差这两块没有复习了，记得那天晚上躺在床上看到了一点多😖 ，第二天早早起床也是提前两个多小时出发，因为适逢上班高峰期，所以过了好多辆公车还是挤不上，好不容易来了一辆，挤上去又被挤下来了😒 ，好在最后终于上了公车，在公车上看到一个好消息，我的<a href="http://wensibo.com.cn/2017/03/12/GitOnAS/" target="_blank" rel="noopener">上一篇文章</a>被<a href="http://mp.weixin.qq.com/s?timestamp=1492142119&amp;src=3&amp;ver=1&amp;signature=*VhrLZu6lG9ft*sn7Btxc-dwlgXhNHqZPffeCQnUFoJJljgYyOlXtbVKdnW7aRwJiAdhY9D-8VInyXLBC-kqO40JRGOQ9SbPXSXhy5SFXtY5CYVUjoWjZOw7ICG5jHFsqhvMlfqwoSz20Gxikwdsd-LbQM3w7nUcbRxGZfn*o1s=" target="_blank" rel="noopener">郭霖的公众号</a>收录啦！心里是十分的开心啊，因为与郭霖大神也交流了一段时间，并且一直将其视为自己学习的目标，所以自己写的文章能够通过他的审核也是十分的开心。也在期待着今天的二面能有好结果。<br>短信通知进入房间面试，二面的面试官有点胖胖，跟一面的瘦瘦面试官一看就不是同种类型的，似乎更难对付，暗自捏了一把汗。<br><strong>我：</strong>敲门，问好，深深鞠躬并双手呈上简历。<br><strong>面试官：</strong>你好请坐，先做个自我介绍吧！<br><strong>我：</strong>跟一面一样，把自我介绍顺利的说了一遍，当然相比一面也适当加了一些内容，因为涉及隐私，所以就不方便写出来啦。<br><strong>面试官：</strong>绩点很高哦(本人专业课学霸)，Android是自学的对吧？学习多久了？<br><strong>我：</strong>跟一面一样，如是回答。<br><strong>面试官：</strong>印象似乎很深刻，觉得我自学能力不错，并且学习成绩好，应该还是个比较有追求的少年。电子商务专业有哪些课程？(又是这个问题，已经麻痹了😒)<br><strong>我：</strong>像上面的一面回答那样，我很真诚的回答面试官的问题，面试官边听边点点头，听到自己在自学操作系统，就抬起头向我微微笑(有点开心😊)。<br><strong>面试官：</strong>说一下你这个Volley项目吧！讲讲你学到了什么？<br><strong>面试官：</strong>像一面一样，我详细地讲解了Volley的实现机理，并且加上一面面试官问到的为什么是5个线程的问题，阐述了自己对这个问题的看法，面试官从看着简历到双眼凝视我。<br><strong>我：</strong>感觉很有戏，不过也预感总监会问一些比较难的问题，所以还是心有余悸。<br><strong>面试官：</strong>在自己做项目的时候是怎么优化代码的？<br><strong>我：</strong>感觉总监面问的就不是一般的问题，因为过了一面所以底子怎么样他应该是心里有数了，所以就问一些宏观上面的问题，考察我开发的一些习惯，这点对于团队合作开发是至关重要的，没有哪个头想招一些编程习惯不好的孩子吧！所以我就向他介绍了Lint，以及日常经常使用的DDMS里面的traceview，heap，allocation tracker，并且分别介绍他们的功能是怎么样的。说完一大堆之后自己舒了一口气，也没有一开始的怯场了。<br><strong>面试官：</strong>应该觉得我的回答不错，我介绍每个工具的用途的时候他就点点头。接着问，如果你来腾讯实习，是想做UI，业务逻辑，还是网络方面的？<br><strong>我：</strong>很开心能够被问到这种问题，因为我对UI很感兴趣，所以我向面试官说到我对UI的敏锐性比较强，并且向他介绍了Google的Material Design设计规范，因为考虑到腾讯旗下的许多产品并没有遵循这个设计规范，所以我也就没有展开比较腾讯的产品(要是说的产品是面试官负责的，不就GG了)，而是将MD设计扯到了我<a href="http://wensibo.com.cn/" target="_blank" rel="noopener">的博客网站</a>，向他说我的网站使用的是Indigo的主题，这个主题充分利用了MD的设计规范，所以我一开始见到它的时候就喜欢上了，并且花了几天几夜的时间开始搭建并且从<a href="http://www.cnblogs.com/ghylzwsb/" target="_blank" rel="noopener">博客园</a>迁移到Hexo。后来因为过于激动，向面试官说起今天郭霖收录了我的文章。(这个细节处理的不是很好，直接向面试官说起了这件事，感觉十分突兀，可能面试官会觉得我有些许轻狂😴)<br><strong>面试官：</strong>礼貌性的笑了笑并且点点头，并且用MBP打开了我的网站，应该有看到吧！问我怎么看Android的UI中的动画？<br><strong>我：</strong>有点懵逼，不知道面试官想问哪方面？就说了Android的三种动画：Tween，Frame，Property(3.0之后推出的)，还说了Activity的进入退出的动画的实现方式，例如使用style定义Activity的淡出淡入效果，还有可以复写Activity的overridePendingTransition方法实现这个效果。<br><strong>面试官：</strong>似乎觉得我说得不错，让我说说我的博客。<br><strong>我：</strong>很开心的向他介绍起整个搭建的过程，并且积极地为Indigo的主题发Issue，解决Bug。<br><strong>面试官：</strong>使用的是Android Studio对吧(因为简历上写了擅长使用Android Studio)，以前用过Eclipse吗？<br><strong>我：</strong>对这方面有很深的体会，因为一开始使用Eclipse很成熟的时候，发现现在很多开发者都是使用AS，但是自己电脑配置又跟不上，并且因为天朝的关系，编译速度巨慢，Gradle的下载就是隔靴搔痒,但是为了解决这一系列的问题，自己读了很多文章，并且看了一本十分不错的书<code>《Android Studio实战 快速、高效地构建Android应用》</code>，向其介绍起书上的内容。<br><strong>面试官：</strong>很满意的看着我，问我平常逛什么网站。<br><strong>我：</strong>平时喜欢逛Github，并且喜欢网络红人<a href="http://gank.io/" target="_blank" rel="noopener">代码家</a>，订阅了邮箱推送，每天会推送一些Github的优秀项目，所以会Fork别人的好项目，另外的话喜欢看<a href="https://developer.android.google.cn/index.html" target="_blank" rel="noopener">Android的中文开发者网站</a>，还向面试官表达了Google推出中文开发者网站的时候自己激动的心情，他很理解的点点头表示赞同。<br><strong>面试官：</strong>绩点很高哦(6/175)，有没有想过要考研啊？<br><strong>我：</strong>跟面试官聊到后来，感觉他都不想问我技术问题了，于是就有点开始得瑟起来，也有点High，这点可能是二面被刷的一个原因吧。于是就说自己对考研兴趣不大，自己更想在兴趣方面(Android开发)有所发展，想早点有立足之地。<br><strong>面试官：</strong>习惯性的还是点点头，可能心里觉得我这个人不太靠谱吧！接着问，如果公司给你提供实习机会，同时学校给你提供保研机会，那你会怎么选择？<br><strong>我：</strong>当然会选择来腾讯实习，因为对考研兴趣不大，想尽快抱住腾讯爸爸的大腿，结交更多的良师益友，让自己更加强大。(现在想起来，觉得回答的有点单薄，也有点让人觉得自己势力心很强，唉😴)<br><strong>面试官：</strong>什么时候可以来实习呢？可以实习多久？<br><strong>我：</strong>我在官网的招聘信息中看到，官方要求是6月~8月，我说6月已经学期结束了，我可以随时到公司实习，实习时间2~3个月都是可以的，听从公司的安排。<br><strong>面试官：</strong>点点头，似乎没什么问题想问了，就问我有什么问题想问我的吗？<br><strong>我：</strong>跟一面一样，我想请面试官评价一下我此次的面试。谢谢！<br><strong>面试官：</strong>说了我的一些好话，什么学习成绩优秀，有项目经验，经常写博客，说要我继续保持下去，如果有下一轮的话，会安排领导的面试和一轮HR的面试，说了很多。<br><strong>我：</strong>心想面试官觉得我还是不错的，不过实际上是不是这样觉得的我就不得而知了，有可能面试官只是不想打击我而已😂。<br><strong>面试官：</strong>还有什么问题吗？<br><strong>我：</strong>愣了一下，心想为什么面试官为什么一直要问我问题？也就没想太多，随便说，我想请问一下面试官在腾讯工作了这么多年了有什么感受吗？<br><strong>面试官：</strong>很慷慨的向我描述他在腾讯的一些经历和感悟，具体的就不方便写出来了。最后还继续问我，还有什么问题想问我的吗？<br><strong>我：</strong>很尴尬的笑了笑，觉得面试官很想我问他问题欸，不过自己实在没有准备其他问题，就说大概就是这两个问题了，谢谢面试官。(现在想想，面试官是在试探我吧，看看我有没有胆量问他问题，因为大部分学生都是处于比较怯场的状态，只会问一些比较平常的问题，既然面试官这么坚要我问他问题，那就是想考验我与其他人有没有不同的地方啊，唉，怪自己太年轻！)</p><p><strong>最后面试官说：“那我把你的简历留下咯！” 我很开心的说好的，非常感谢面试官对我的面试，深深鞠躬然后离开。回到学校之后一直在等待三面的通知，以为会像一面一样晚上8点多发短信通知，但是直到第二天中午还是没有消息，自己还是很着急的。第二天下午上课的时候查了一下简历的状态，显示自己的此次面试已经结束了。</strong></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>得知自己错过三面的时候突然间觉得有点懵和失望，不过也还是接受了这个结果。可能是二面的时候自己有一些表达上面需要更加斟酌，并且在向面试官提问上面需要更加大胆一点吧！主要有下面几点吧：</p><blockquote><ul><li>更加深厚的底子，同等实力看学校，同等水平看颜值(微笑脸，自己两者都沾不到边)。</li><li>就我个人而言，我对比较新的开源框架接触较少，不过这段时间有在认真刻苦学习了，过些日子会写一些文章总结；另外本人项目经验几乎为0，唉！好学校好专业还是重要的，不过我最近也在做一个稍微复杂的项目，希望下次面试的时候能派上用场。</li><li>面试过程中需要更加沉稳，好好回答每个问题，不能够轻浮。越简单的问题越应该引起注意，而不是自我感觉良好，整个人要飘起来了。</li><li>多发挥自己的优势（我的话是博客可能写的好一点，当然说自己的优势时不要太得瑟！切忌！)。</li><li>面试官想你问他问题的时候，应该表现得跟其他面试者不一样，记住每个问题都是展现自己与众不同的机会，好好把握。</li><li>不要因为一次的失败就停滞不前，可以不开心一阵，但是要重整旗鼓，对下次面试发起挑战！</li></ul></blockquote><h1 id="推荐"><a href="#推荐" class="headerlink" title="推荐"></a>推荐</h1><p>我想向大家推荐一下一个非计算机专业的学生是如何自学Android的，不过这仅仅代表我个人的学习方式，绝不是最好的。</p><h2 id="书籍"><a href="#书籍" class="headerlink" title="书籍"></a>书籍</h2><blockquote><ul><li><a href="https://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.6.4M6BQ4&amp;id=542237674970&amp;user_id=1664456249&amp;cat_id=2&amp;is_b=1&amp;rn=fd2416db11514d651ffe9d4c6a38202a" target="_blank" rel="noopener">《第一行代码》(第二版)</a></li><li><a href="https://item.jd.com/11939710.html?dist=jd" target="_blank" rel="noopener">《Android编程权威指南》（第2版）</a></li><li><a href="https://item.jd.com/11791229.html?dist=jd" target="_blank" rel="noopener">《App研发录》</a></li><li><a href="https://item.jd.com/10831235.html?dist=jd" target="_blank" rel="noopener">《深入理解Android》（卷1）</a></li><li><a href="https://item.jd.com/11760209.html?dist=jd" target="_blank" rel="noopener">《Android开发艺术探索》</a></li><li><a href="http://item.jd.com/12001016.html?dist=jd" target="_blank" rel="noopener">《Android Studio实战 快速、高效地构建Android应用》</a></li><li><a href="https://item.jd.com/10057319.html?dist=jd" target="_blank" rel="noopener">《设计模式：可复用面向对象软件的基础》</a></li><li><a href="https://item.jd.com/10058902.html?dist=jd" target="_blank" rel="noopener">《Effective Java》</a></li><li><a href="http://item.jd.com/11733256.html?dist=jd" target="_blank" rel="noopener">《GitHub入门与实践》</a></li></ul></blockquote><h2 id="网站"><a href="#网站" class="headerlink" title="网站"></a>网站</h2><blockquote><ul><li><a href="https://developer.android.google.cn/index.html" target="_blank" rel="noopener">Android开发者中文网站</a></li><li><a href="http://blog.csdn.net/guolin_blog" target="_blank" rel="noopener">郭霖的专栏</a></li><li><a href="http://blog.csdn.net/lmj623565791" target="_blank" rel="noopener">鸿洋博客</a></li><li><a href="https://github.com/github" target="_blank" rel="noopener">GitHub</a></li></ul></blockquote><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><blockquote><ul><li>勤奋打码</li><li>勤奋打码</li><li>勤奋打码</li><li>重要的事情说三遍</li></ul></blockquote><h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>这篇文章是我第一次面试后记录的，这只能是我个人的经验总结，并不一定全部对，也不一定适合所有人，所以如果大家觉得我有哪些讲得不好的请给我<a href="http://wensibo.com.cn/" target="_blank" rel="noopener">留言</a>，希望我们能够继续交流。也祝大家在面试工作中好运！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;本篇文章已授权微信公众号 guolin_blog （郭霖）独家发布&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;或许你已经看过这篇文章，但我想告诉大家的是其实我并没有大家说的那么厉害，本人学到的还只是皮毛中的皮毛，相比起很多大神我自己只有负责仰望的权利，所以如果可以的话请在本篇文章的最后留下你对我的建议，记住是建议！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先做个自我介绍，本人大三狗一枚，就读的是广州一个普通的一本大学(非985、211)，专业是比较尴尬的电子商务(非计算机学院，连C的课程都没有就只有Java)，但是一切的尴尬并没有阻挡我对Android开发的热爱，自学一年多了，基础以及开发的技术也掌握的相对成熟了。在即将要变身大四狗的关键节点看到了腾讯爸爸暑期实习生的招聘，便刻不容缓地参加笔试，很开心顺利收到面试通知，可惜最终止步二面，以下是我此次面试的整个过程，希望对大家有所帮助。&lt;/p&gt;

			&lt;script&gt;
				console.error(&quot;Error: [hexo-tag-aplayer] Unrecognized tag argument(2): autoplay=ture&quot;);
			&lt;/script&gt;
    
    </summary>
    
      <category term="腾讯面经" scheme="http://www.wensibo.top/categories/%E8%85%BE%E8%AE%AF%E9%9D%A2%E7%BB%8F/"/>
    
    
      <category term="面经" scheme="http://www.wensibo.top/tags/%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>或许是介绍Android Studio使用Git最详细的文章</title>
    <link href="http://www.wensibo.top/2017/03/12/GitOnAS/"/>
    <id>http://www.wensibo.top/2017/03/12/GitOnAS/</id>
    <published>2017-03-12T06:18:25.000Z</published>
    <updated>2018-10-21T08:26:20.712Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>本篇文章已授权微信公众号 guolin_blog （郭霖）独家发布</strong><br><strong>本文较长，图片很多很多，流量党慎入</strong><br>使用Git已经有一段时间了，但是之前都是使用<code>Git Bash</code>，在Android Studio上使用Git一开始不是很习惯，就像用惯了SVN来使用Git一样，琢磨了一段时间的Android Studio，也看了我觉得为数不多但是很有质量的介绍AS的书籍 <code>《Android Studio实战 快速、高效地构建Android应用》</code>，强烈安利大家哦！所以就写了这篇文章跟大家一起学习如何在Android Studio高效地使用Git。另外如果大家想要拿来学习的话可以直接fork<a href="https://github.com/Wensibob/MyGit" target="_blank" rel="noopener">我的项目</a> 。<br>另外一点需要说明的就是本文中大量使用Android Studio的快捷键，如果你不熟悉，可以看看我的<a href="http://www.wensibo.top/2017/03/09/Android%20Studio%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%80%BB%E7%BB%93/">另外一篇文章</a> 。<br><a id="more"></a></p><h2 id="安装Git"><a href="#安装Git" class="headerlink" title="安装Git"></a>安装Git</h2><p>我使用的是Windows，所以我这里只能介绍Windows下安装Git的过程了。<br>点<a href="https://pan.baidu.com/s/1kU5OCOB#list/path=%2Fpub%2Fgit" target="_blank" rel="noopener">这里</a>选择符合你的版本，直接安装就行了。<br>安装完成之后，在开始菜单中找到<code>Git--&gt;Git Bash</code>，如果出现类似下面的对话框的话就证明安装成功了。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/1.png" alt="Git界面" title="">                </div>                <div class="image-caption">Git界面</div>            </figure><br>安装成功之后，需要配置一下你的信息，直接在上面的命令行中输入以下命令：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global user.name &quot;Your Name&quot;</span><br><span class="line">$ git config --global user.email &quot;email@example.com&quot;</span><br></pre></td></tr></table></figure></p><blockquote><p><strong>注意：<br>1.将上方的<code>Your Name</code>换成你自己的名字，随意起都行。<br>2.将上方的<a href="mailto:`email@example.com" target="_blank" rel="noopener">`email@example.com</a><code>换成你自己的邮箱。3.讲一下</code>git config<code>命令的</code>–global`参数，用了这个参数，表示你这台机器上所有的Git仓库都会使用这个配置，当然也可以对某个仓库指定不同的用户名和Email地址。</strong></p></blockquote><h2 id="Android-Studio配置Git"><a href="#Android-Studio配置Git" class="headerlink" title="Android Studio配置Git"></a>Android Studio配置Git</h2><p>使用快捷键<code>Ctrl+Alt+S</code>打开Settings，接着点击<code>Version Control --&gt; Git</code>在<code>Path To Git Executable</code>上输入Git的存放位置，如下图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/2.png" alt="Git Path" title="">                </div>                <div class="image-caption">Git Path</div>            </figure><br>之后点击旁边的Test按钮，如果出现如下的成功提示，说明Git配置成功。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/3.png" alt="Git Success" title="">                </div>                <div class="image-caption">Git Success</div>            </figure><br>接着按照下图配置你的Github账号密码，输入完成之后点击Test按钮，如果账号密码都正确的话就会提示成功，那么就可以继续往下走了：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/4.png" alt="Github Account" title="">                </div>                <div class="image-caption">Github Account</div>            </figure></p><h2 id="创建本地仓库"><a href="#创建本地仓库" class="headerlink" title="创建本地仓库"></a>创建本地仓库</h2><p>不管你是刚刚新建的项目，亦或是已经写好的项目，进行的操作都是一样的。<br>这里我以新建一个名为<a href="https://github.com/Wensibob/MyGit" target="_blank" rel="noopener">MyGit</a>的项目为例子，如果你想练习，也可以到<a href="https://github.com/Wensibob/MyGit" target="_blank" rel="noopener">这里</a>fork一下。<br>对于一个已经新建好的项目，找到菜单栏上的VCS，按照下图进行操作：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/5.png" alt="创建本地仓库" title="">                </div>                <div class="image-caption">创建本地仓库</div>            </figure><br>选择项目的根目录为git初始化的目录：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/6.png" alt="初始化目录" title="">                </div>                <div class="image-caption">初始化目录</div>            </figure><br>初始化之后你会发现原本文件的文件名都是<strong>白色</strong>的，现在变成了<strong>棕色</strong>，这表示文件已经被git跟踪了，但是并没有添加到仓库中：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/7.png" alt="棕色目录" title="">                </div>                <div class="image-caption">棕色目录</div>            </figure></p><h2 id="忽略文件"><a href="#忽略文件" class="headerlink" title="忽略文件"></a>忽略文件</h2><p>我们知道，在git初始化一个仓库的时候会自动生成一个.gitignore文件，这个文件用来忽略那些不用加入到仓库的文件，在我们这个工程中总共生成了两个.gitignore文件，分别是在项目根目录下，以及在app文件夹下。我们可以对这些文件进行编辑，表示我们需要忽略哪些文件，但是一般情况下，我们选择默认就好，除非你有需要就进行适当的修改：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/8.png" alt=".gitignore" title="">                </div>                <div class="image-caption">.gitignore</div>            </figure></p><h2 id="添加文件"><a href="#添加文件" class="headerlink" title="添加文件"></a>添加文件</h2><p>添加文件就如git命令中的<code>git add</code>，在Android Studio中add的方式有四种，但是都是大同小异，让我来一一举例：</p><p><strong>1.选中项目的根目录，右键选中Git，再选中Add，如下图：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/9.png" alt="第一种add方式" title="">                </div>                <div class="image-caption">第一种add方式</div>            </figure><br><strong>2.选中项目根目录，点击菜单栏中的VCS菜单，选中Git–&gt;Add，如下图：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/10.png" alt="第二种add方式" title="">                </div>                <div class="image-caption">第二种add方式</div>            </figure><br><strong>3.使用快捷键<code>Alt+9</code>，或者点击屏幕下方的Version Control工具按钮</strong><br>打开版本控制的窗口，可以看到应该是如下图的样子，该窗口有两个下拉栏，一个是Default，用来记录已经添加的文件，另一个是Unversioned files，用来记录已经被跟踪但是未添加的文件。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/11.png" alt="Version Control" title="">                </div>                <div class="image-caption">Version Control</div>            </figure><br>右键Unversioned files，选中Git–&gt;Add，如下图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/12.png" alt="第三种add方式" title="">                </div>                <div class="image-caption">第三种add方式</div>            </figure><br><strong>4.使用快捷键<code>Ctrl+Alt+A</code></strong></p><p>add之后文件名变成了<strong>绿色</strong>，这是代表已经添加进仓库为文件，接下来就可以commit文件了，使用快捷键<code>Ctrl+K</code>或者选中工程根目录右键<code>Git--&gt;Commit Directory</code>可以调出commit窗口，如下所示，在其中选择你想要提交的文件，填写提交的信息，在Author文本框中可以填写提交此次提交的操作者名字，如果不填写的话，就会默认是之前配置Github账号的用户名。可以看到，提交之后文件名重新变回了熟悉的<strong>白色</strong>。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/13.png" alt="首次提交" title="">                </div>                <div class="image-caption">首次提交</div>            </figure><br>提交成功之后，你可以使用快捷键<code>Alt+9</code>，或者点击屏幕下方的Version Control工具按钮，切换到Log菜单查看Log日志，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/17.png" alt="首次提交Log日志" title="">                </div>                <div class="image-caption">首次提交Log日志</div>            </figure></p><h2 id="如何clone项目"><a href="#如何clone项目" class="headerlink" title="如何clone项目"></a>如何clone项目</h2><p>使用Git clone项目到本地中是很简单的，在Android Studio中也是如此，首先找到你喜欢的项目，fork到你自己的仓库之后，点击Clone or Download按钮，复制地址，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/14.png" alt="github 复制地址" title="">                </div>                <div class="image-caption">github 复制地址</div>            </figure><br>接下来回到Android Studio，按照下图的操作可以打开clone的对话框，在地址栏中粘贴刚才复制的地址，点击Test按钮，测试是否可以通过，如果成功，那么久可以点击Clone导入项目了。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/15.png" alt="clone项目" title="">                </div>                <div class="image-caption">clone项目</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/16.png" alt="Clone Test" title="">                </div>                <div class="image-caption">Clone Test</div>            </figure></p><hr><blockquote><p><strong> 回到我们最开始讲的<a href="https://github.com/Wensibob/MyGit" target="_blank" rel="noopener">MyGit项目</a>，首先我们需要模仿一下在日常开发中使用Git的情形，例如我们经常会在分支上进行工作，所以熟练地掌握分支工作的流程以及技巧是十分重要的，接下来我会使用具体的例子跟大家一起了解如何在Android Studio上使用分支进行开发。<br>以下举得例子将会围绕Git的一种工作模式，即：<code>Git Flow</code>，如果你对此不是很了解，那么推荐你<a href="http://www.ruanyifeng.com/blog/2015/12/git-workflow.html" target="_blank" rel="noopener">阮一峰老师的文章</a> 。</strong></p></blockquote><hr><h2 id="Git-flow"><a href="#Git-flow" class="headerlink" title="Git flow"></a>Git flow</h2><p>Git flow是广泛采用的一种工作流程<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/git-workflow.png" alt="Git_flow工作流程图" title="">                </div>                <div class="image-caption">Git_flow工作流程图</div>            </figure><br>他的主要特点有两个：</p><p><strong>1.首先，项目存在两个长期分支</strong></p><blockquote><ul><li>主分支master</li><li>开发分支dev</li></ul></blockquote><p>前者用于存放对外发布的版本，任何时候在这个分支拿到的，都是稳定的分布版；后者用于日常开发，存放最新的开发版。</p><p><strong>2.其次，项目存在三种短期分支</strong></p><blockquote><ul><li>功能分支（feature branch）</li><li>补丁分支（hotfix branch）</li><li>预发分支（release branch）</li></ul></blockquote><h2 id="新增功能1——显示Hello-World"><a href="#新增功能1——显示Hello-World" class="headerlink" title="新增功能1——显示Hello World"></a>新增功能1——显示Hello World</h2><p>正如我们在介绍Git flow介绍的，master分支只是用于产品的发布，在平时的开发中是不会使用它的，而只会使用dev分支，但是如果我们有了新的功能，一般是会在dev分支中在创建一条该功能的分支，所以我们应该这样做。</p><h3 id="创建dev、feature-1分支，并且我们需要转到feature-1分支上"><a href="#创建dev、feature-1分支，并且我们需要转到feature-1分支上" class="headerlink" title="创建dev、feature-1分支，并且我们需要转到feature-1分支上"></a>创建dev、feature-1分支，并且我们需要转到feature-1分支上</h3><p>在Android Studio中，我们可以很方便的管理分支，在主界面的右下角，点击Git可以出现当前的分支，默认为master，我们选中<code>New Branch</code>，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/19.png" alt="add_branch_feature-1" title="">                </div>                <div class="image-caption">add_branch_feature-1</div>            </figure><br>在弹出的对话框中我们输入<code>feature-1</code>，点击OK，这样我们不仅新建了feature-1分支，并且正处于该分支中，接下来按照同样的方法创建dev分支，如果不出意外的话，我们现在应该是处于dev分支上，但是因为我们现在要开发功能1，所以必须转换到<code>feature-1</code>分支上，按照下图的操作，我们能够回到feature-1分支上。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/20.png" alt="come back to feature-1" title="">                </div>                <div class="image-caption">come back to feature-1</div>            </figure><br>接下来打开Log，我们应该能够看到如下的情景：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/21.png" alt="log for add two branches" title="">                </div>                <div class="image-caption">log for add two branches</div>            </figure><br>可以看到我们现在有三个分支：master、dev、feature-1，但是AS提示我们应该有四条分支，其实HEAD就是当前活跃分支的游标。形象的记忆就是：你现在在哪儿，HEAD 就指向哪儿，所以 Git 才知道你在那儿！不过 HEAD 并非只能指向分支的最顶端，实际上它可以指向任何一个节点，它就是 Git 内部用来追踪当前位置的标记。我们可以使用下面的图来演示当前分支的情况：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/branch_picture_1.png" alt="branch_picture_1" title="">                </div>                <div class="image-caption">branch_picture_1</div>            </figure></p><h3 id="完成功能1"><a href="#完成功能1" class="headerlink" title="完成功能1"></a>完成功能1</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * feature-1</span></span><br><span class="line"><span class="comment">     * display HelloWorld</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">feature1</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (btn_feature_1.getText().toString().equals(<span class="string">"功能1"</span>)) &#123;</span><br><span class="line">            btn_feature_1.setText(R.string.feature_1_dis);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            btn_feature_1.setText(R.string.feature_1);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>好了，功能1编写完成，那么就提交吧！老方法：Ctrl+K进行提交，按照下图填写提交信息，每一次的提交信息最好能够详细并且格式规范，这样以后再查看Log的时候就会比较方便。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/22.png" alt="add feature-1 on feature-1" title="">                </div>                <div class="image-caption">add feature-1 on feature-1</div>            </figure><br>点击Commit按钮提交完毕之后，可以看到现在的log图变成了下图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/23.png" alt="log after add feature-1" title="">                </div>                <div class="image-caption">log after add feature-1</div>            </figure><br>我们可以打开Log图的右侧，他列出了目前正在被Git跟踪的所有文件，我们选中MainActivity.java，点击上边的第二个按钮<code>Show Diff(显示差异)</code>：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/24.png" alt="show diff" title="">                </div>                <div class="image-caption">show diff</div>            </figure><br>在弹出的界面中可以显示该文件的当前版本和master分支中的该文件的差异，我们可以使用<code>Esc</code>快捷键退出该界面，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/25.png" alt="diff1 on MainActivity.java" title="">                </div>                <div class="image-caption">diff1 on MainActivity.java</div>            </figure></p><h3 id="将feature-1分支合并到dev分支上"><a href="#将feature-1分支合并到dev分支上" class="headerlink" title="将feature-1分支合并到dev分支上"></a>将feature-1分支合并到dev分支上</h3><p>如下图所示，选中dev分支并选择checkout：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/26.png" alt="chose dev" title="">                </div>                <div class="image-caption">chose dev</div>            </figure><br>你会发现现在代码回到了最开始的状态，现在选中feature-1分支并选中merge，准备将feature-1分支合并到dev分支上：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/27.png" alt="merge feature-1 to dev" title="">                </div>                <div class="image-caption">merge feature-1 to dev</div>            </figure><br>再来重新看一下Log图，他长下面这样，可以看到现在feature-1分支已经与dev分支合并，并且现在他们是处于同一状态的：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/28.png" alt="log after merge to dev" title="">                </div>                <div class="image-caption">log after merge to dev</div>            </figure><br>最后的最后，我们需要删除已经完成任务的feature-1分支，以免分支过多管理混乱。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/29.png" alt="delete branch feature-1" title="">                </div>                <div class="image-caption">delete branch feature-1</div>            </figure></p><h2 id="新增功能2——显示Hello-Android"><a href="#新增功能2——显示Hello-Android" class="headerlink" title="新增功能2——显示Hello Android"></a>新增功能2——显示Hello Android</h2><p>同样的道理，我们可以按照如下的步骤这样做：</p><p><strong>1.迁出dev分支，并且新建分支<code>feature-2</code>之后迁出<code>feature-2</code>分支，具体的步骤我就不再演示了，跟上面的是一样的。</strong></p><p><strong>2.开始撸代码。</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * feature-2</span></span><br><span class="line"><span class="comment">    * display HelloAndroid</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">feature2</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (btn_feature_2.getText().toString().equals(<span class="string">"功能2"</span>)) &#123;</span><br><span class="line">           btn_feature_2.setText(R.string.feature_2_dis);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           btn_feature_2.setText(R.string.feature_2);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><p><strong>3.使用快捷键Ctrl+K提交feature-2的修改，并填写提交信息。</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/30.png" alt="add feature-2 on branch feature-2" title="">                </div>                <div class="image-caption">add feature-2 on branch feature-2</div>            </figure><br><strong>4.切换回dev分支并且合并feature-2分支之后删除feature-2分支。</strong><br>如果不出错的话，你的Log应该是下面这样的：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/31.png" alt="log after delete feature-2" title="">                </div>                <div class="image-caption">log after delete feature-2</div>            </figure></p><h2 id="在dev分支上增加功能3——Hello-Java"><a href="#在dev分支上增加功能3——Hello-Java" class="headerlink" title="在dev分支上增加功能3——Hello Java"></a>在dev分支上增加功能3——Hello Java</h2><p>同样的道理，这里我就不再一一演示，如果按照上面的步骤做的话，最后的Log应该是这样的：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/32.png" alt="log after add feature-3 on dev" title="">                </div>                <div class="image-caption">log after add feature-3 on dev</div>            </figure></p><h2 id="重点来了，老板说了，新开发的功能3不喜欢，需要删除了功能3，咋办呢？"><a href="#重点来了，老板说了，新开发的功能3不喜欢，需要删除了功能3，咋办呢？" class="headerlink" title="重点来了，老板说了，新开发的功能3不喜欢，需要删除了功能3，咋办呢？"></a>重点来了，老板说了，新开发的功能3不喜欢，需要删除了功能3，咋办呢？</h2><p>这里就需要讲到Git的回退了，在Android Studio中提供了两种回退的方式：Git revert以及Git reset。</p><h3 id="Git-revert"><a href="#Git-revert" class="headerlink" title="Git revert"></a>Git revert</h3><p>Git revert可以将版本回退到上一步，但是会新增一个提交，他的流程就像下面这幅图一样：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/git_revert_picture_2.png" alt="git_revert_picture_2" title="">                </div>                <div class="image-caption">git_revert_picture_2</div>            </figure><br><strong>1.首先打开Log，找到功能3的提交，右键选择复制哈希码(Copy Revision Number)，如下图所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/34.png" alt="Copy Revision Number" title="">                </div>                <div class="image-caption">Copy Revision Number</div>            </figure><br><strong>2.打开Android Studio的终端Terminal，他就在Version Control的旁边，之后输入以下命令按回车键：</strong><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git revert 9c834d8c66598fb132a0cc8e4c1f8c341d058f3e</span><br></pre></td></tr></table></figure></p><blockquote><p>那一段很复杂的数字字母就是我们刚才复制的哈希码</p></blockquote><p>如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/35.png" alt="git revert on terminal" title="">                </div>                <div class="image-caption">git revert on terminal</div>            </figure><br><strong>3.之后终端会列出此次提交的具体信息，如果确认要回退，请输入<code>:q</code>保存此次操作并且退出会话<br>现在你可以看到，他确实增加了一次提交，并且回到了上一版的内容，Log应该是这样的：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/36.png" alt="log after revert" title="">                </div>                <div class="image-caption">log after revert</div>            </figure></p><h3 id="Git-reset"><a href="#Git-reset" class="headerlink" title="Git reset"></a>Git reset</h3><p>相比之下，Git reset就要干脆的多，与Git revert的功能一样，它也可以将代码恢复到上一个版本，但是不会新增一次提交，他的流程如下：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/git_reset_picture_3.png" alt="git_reset_picture_3" title="">                </div>                <div class="image-caption">git_reset_picture_3</div>            </figure><br>因为我们需要删除功能3，并且让Log看起来并没有revert的这一次提交，所以我们应该在dev分支上后退两步，确实是这样的对吧！</p><p><strong>1.点击菜单栏上的<code>VCS--&gt;Git--&gt;Reset HEAD</code>，打开对话框，在To Commit文本框中输入<code>HEAD~2</code>，就像下图这样：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/37.png" alt="reset HEAD" title="">                </div>                <div class="image-caption">reset HEAD</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/38.png" alt="reset HEAD~2" title="">                </div>                <div class="image-caption">reset HEAD~2</div>            </figure><br><strong>2.点击Reset按钮之后，你可以发现Log变了，变回原来那个熟悉的画面了：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/31.png" alt="log after reset" title="">                </div>                <div class="image-caption">log after reset</div>            </figure></p><h2 id="master分支上被修改了"><a href="#master分支上被修改了" class="headerlink" title="master分支上被修改了"></a>master分支上被修改了</h2><p>突然你发现你的同事在master分支上提交了两次，分别是增加了功能1和功能4，但是其中的功能1很显然<code>HelloWorld</code>被写成了<code>WorldHello</code>，例如这样的：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * feature-1</span></span><br><span class="line"><span class="comment">    * display WorldHello</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">feature1</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">       btn_feature_1.setText(R.string.feature_1_dis);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * feature-4</span></span><br><span class="line"><span class="comment">    * display Hello</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">feature4</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">       btn_feature_4.setText(R.string.feature_4_dis);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><p>提交更改，之后Log应该是这样的：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/40.png" alt="log after add feature-1&4 on master" title="">                </div>                <div class="image-caption">log after add feature-1&4 on master</div>            </figure><br>我们依旧用演示图表示当前分支的发展情况：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/branch_picture_4.png" alt="branch_picture_4" title="">                </div>                <div class="image-caption">branch_picture_4</div>            </figure></p><h2 id="Rebase"><a href="#Rebase" class="headerlink" title="Rebase"></a>Rebase</h2><p>老板说了，master分支只要功能4不需要功能1，而dev分支上的功能1、2全部都要合并到master分支上。那么这个时候我们就可以使用rebase了。<br>git rebase用于把一个分支的修改合并到当前分支。现在我们切换到master分支，将dev上的做修改加入到master中，所以我们选择rebase，在Android Studio中提供了功能十分强大的rebase。</p><p><strong>1.点击菜单栏上的VCS–&gt;Git–&gt;Rebase，如下图所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/41.png" alt="git rebase" title="">                </div>                <div class="image-caption">git rebase</div>            </figure><br><strong>2.在弹出的对话框中，我们在Onto的下拉栏中选中dev分支，表示我们需要将master分支rebase到该分支下。</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/42.png" alt="git rebase branch" title="">                </div>                <div class="image-caption">git rebase branch</div>            </figure><br><strong>3.点击<code>Rebase</code>之后，你会发现Android Studio弹出对话框，显示master分支的两次提交，需要我们做出选择，如下图所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/43.png" alt="git start rebasing" title="">                </div>                <div class="image-caption">git start rebasing</div>            </figure><br><strong>4.因为我们不需要master分支上的功能1但是需要功能4，所以在功能1的提交上我们选择skip(跳过这个提交)，在提交4上选择pick(挑选此次这个提交)，点击 <code>Start Rebasing</code>，我们可以看到又有对话框弹出，此次是让我们对每个文件进行挑选，如下图所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/44.png" alt="rebase conflict" title="">                </div>                <div class="image-caption">rebase conflict</div>            </figure><br><strong>5.对于每一个文件，你可以选择接受你的那一部分，或者接受另一只分支上的内容，又或者你可以点击Merge对文件进行筛选。我们点击<code>Merge</code>按钮，可以看到有三个文件呈现在屏幕上。其中中间的文件是最后的结果，左边的为当前分支master分支，右边的为dev分支，你还可以发现在修改过的每一行中都存在一个<code>X &gt;&gt;</code>符号，点击<code>X</code>表示不需要这一行的修改，点击<code>&gt;&gt;</code>表示接受这一行的修改，我们甚至还可以像在编辑器中那样复制、粘贴、编辑内容，我们最终作出的选择如下图所示，之后可以点击Apply进行保存，如果你不想保存，那就点击Abort终止此次修改：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/45.png" alt="conflict between files" title="">                </div>                <div class="image-caption">conflict between files</div>            </figure></p><p><strong>6.对于剩下的两个文件也做相同的处理，之后我们可以看到master分支已经有了dev分支的功能1和功能2和自身的功能4，并且去掉了自己之前的功能1，可以看一下Log，如下所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/46.png" alt="log after rebase" title="">                </div>                <div class="image-caption">log after rebase</div>            </figure><br>我们依旧使用演示图来表示最后的分支情况：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/branch_picture_5.png" alt="branch_picture_5" title="">                </div>                <div class="image-caption">branch_picture_5</div>            </figure></p><h2 id="推送到远程仓库"><a href="#推送到远程仓库" class="headerlink" title="推送到远程仓库"></a>推送到远程仓库</h2><p>推送很简单，你可以导航到菜单栏<code>VCS--&gt;Import Into Version Control --&gt;Share Project on Github</code>，如下图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/47.png" alt="push" title="">                </div>                <div class="image-caption">push</div>            </figure><br>在弹出的对话框中填写远程仓库的名称，点击Share：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/48.png" alt="share" title="">                </div>                <div class="image-caption">share</div>            </figure><br>之后你就可以在你的<a href="https://github.com/Wensibob/MyGit" target="_blank" rel="noopener">Github</a>上面看到这一次的推送了。</p><h2 id="本地所做的修改如何同步到远程仓库"><a href="#本地所做的修改如何同步到远程仓库" class="headerlink" title="本地所做的修改如何同步到远程仓库"></a>本地所做的修改如何同步到远程仓库</h2><p>现在我需要在工程中加入一些文件，例如说我新建了一个<code>screenshots</code>文件夹，并在其中添加了这篇文章需要用到的截图，那么如何将这些文件一起同步到远程仓库呢？其实很简单。</p><p><strong>1.使用快捷键<code>Alt+9</code>或者点击工具按钮打开<code>Version Control</code>，右键<code>Unversioned Files</code>，选择<code>Git--&gt;Add</code>，将所有文件加入Git中，如下图：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/49.png" alt="add files" title="">                </div>                <div class="image-caption">add files</div>            </figure><br><strong>2.右键Default，选择Commit所有的文件之后填写提交信息，如下图：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/50.png" alt="commit files" title="">                </div>                <div class="image-caption">commit files</div>            </figure><br><strong>3.使用快捷键<code>Ctrl+Shift+K</code>或者右键工程根目录，选择push项目，如下图所示：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/51.png" alt="push project" title="">                </div>                <div class="image-caption">push project</div>            </figure><br><strong>4.在弹出的对话框中点击<code>Push</code>按钮，就可以将所做的修改同步到远程仓库了，如下图：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-12Git/52.png" alt="push!" title="">                </div>                <div class="image-caption">push!</div>            </figure><br><strong>5.对于修改过的文件想要同步到远程仓库，按照同样的步骤就行了，这里不再赘述了。</strong></p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这篇文章有点长，图也很多，我也写了很久，有的时候思路不清晰也混了(尴尬脸)，所以难免会有错误，还请大家批评指正，大家互相学习，希望你能够学到更多Android Studio的知识。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;本篇文章已授权微信公众号 guolin_blog （郭霖）独家发布&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;本文较长，图片很多很多，流量党慎入&lt;/strong&gt;&lt;br&gt;使用Git已经有一段时间了，但是之前都是使用&lt;code&gt;Git Bash&lt;/code&gt;，在Android Studio上使用Git一开始不是很习惯，就像用惯了SVN来使用Git一样，琢磨了一段时间的Android Studio，也看了我觉得为数不多但是很有质量的介绍AS的书籍 &lt;code&gt;《Android Studio实战 快速、高效地构建Android应用》&lt;/code&gt;，强烈安利大家哦！所以就写了这篇文章跟大家一起学习如何在Android Studio高效地使用Git。另外如果大家想要拿来学习的话可以直接fork&lt;a href=&quot;https://github.com/Wensibob/MyGit&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;我的项目&lt;/a&gt; 。&lt;br&gt;另外一点需要说明的就是本文中大量使用Android Studio的快捷键，如果你不熟悉，可以看看我的&lt;a href=&quot;http://www.wensibo.top/2017/03/09/Android%20Studio%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%80%BB%E7%BB%93/&quot;&gt;另外一篇文章&lt;/a&gt; 。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Android Studio" scheme="http://www.wensibo.top/tags/Android-Studio/"/>
    
      <category term="Git" scheme="http://www.wensibo.top/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>Android Studio快捷键总结</title>
    <link href="http://www.wensibo.top/2017/03/09/Android%20Studio%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%80%BB%E7%BB%93/"/>
    <id>http://www.wensibo.top/2017/03/09/Android Studio快捷键总结/</id>
    <published>2017-03-09T05:30:25.000Z</published>
    <updated>2018-10-21T08:26:20.548Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>Android Studio2014年已经正式发布了，但是前几天才更新到2.3的版本，据说很多人升级了之后出现很多不可预见的bug，我还是乖乖守好我的2.2.3，之篇文章我和大家一起分享在使用Android Studio过程中经常会用到的以及许多能够提高效率但是我个人不经常用的快捷键，需要注意的是我只介绍Windows操作环境下的快捷键，理论上来说Windows与Linux用户在快捷键上是相同的，但是Mac用户就会比较特殊，毕竟有钱人都比较特殊对吧😜，我一个穷屌丝就暂时下不了微软大哥的船了。对了，我还会用粗体的形式标出经常会使用到的快捷键，很多时候你会觉得背这些快捷键确实是一件很烦人的事情，但是最好是实践出真知，不要硬性背，或许能够有更好的效果。<br><a id="more"></a></p><h1 id="Windows系统下Android-Studio快捷键总结"><a href="#Windows系统下Android-Studio快捷键总结" class="headerlink" title="Windows系统下Android Studio快捷键总结"></a>Windows系统下Android Studio快捷键总结</h1><table><thead><tr><th style="text-align:left">快捷键,加粗的是经常会使用的</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Ctrl+E</strong></td><td style="text-align:left"><strong>打开最近操作的文件</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Z</strong></td><td style="text-align:left"><strong>撤销</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Shift+Z</strong></td><td style="text-align:left"><strong>重做(在Eclipse中使用的是Ctrl+Y)</strong></td></tr><tr><td style="text-align:left"><strong>Ctr+Y</strong></td><td style="text-align:left"><strong>删除该行(Eclipse中的删除该行是Ctrl+D)</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+D</strong></td><td style="text-align:left"><strong>向下复制该行</strong></td></tr><tr><td style="text-align:left">F11</td><td style="text-align:left">将鼠标停留的所在行加入书签Bookmarks中，可以使用快捷键Alt+2查看所有的书签以及Favorites和断点BreakPoints</td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+向左/右箭头</strong></td><td style="text-align:left"><strong>遍历你的导航操作(如光标移动、选项卡切换和文件打开)</strong></td></tr><tr><td style="text-align:left"><strong>Alt+左右箭头</strong></td><td style="text-align:left"><strong>可以在所有已经打开的文件中左右切换</strong></td></tr><tr><td style="text-align:left"><strong>Alt+数字1、2、3、4、5、6···</strong></td><td style="text-align:left"><strong>可以打开相对应的工具按钮(分布在Android Studio的左、下、右边边，并且其中一些按钮还有一个对应的小数字)，使用Alt+对应的数字就能打开或者关闭这些按钮了</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Shift+V</strong></td><td style="text-align:left"><strong>复制历史记录</strong></td></tr><tr><td style="text-align:left">Ctrl+Alt+Shift+C</td><td style="text-align:left">复制方法、变量、或类的逻辑引用，当把此引用粘贴到另一个源文件中时能够自动导入所需的限定符和导入语句</td></tr><tr><td style="text-align:left"><strong>Ctrl+Shift+A</strong></td><td style="text-align:left"><strong>弹出的搜索框中输入你想搜索的内容(Android Studio的具备的特性、功能)，系统会给出结果，这货很有用😊</strong></td></tr><tr><td style="text-align:left">Alt+F1</td><td style="text-align:left">出现Select In 对话框，可以进行一些便捷的操作，虽然好用但是还不如用鼠标</td></tr><tr><td style="text-align:left"><strong>Ctrl+N</strong></td><td style="text-align:left"><strong>在弹出的对话框中输入类的名字可以直接打开该类，超赞😊</strong></td></tr><tr><td style="text-align:left">Ctrl+Shift+N</td><td style="text-align:left">在弹出的对话框中输入文件的名字可以直接打开该文件，比起上一个能打开的文件更多</td></tr><tr><td style="text-align:left"><strong>Ctrl+G</strong></td><td style="text-align:left"><strong>可以输入想要到达的行数或者行数:列数，文件很长的时候很有用</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+Home</strong></td><td style="text-align:left"><strong>可以打开与当前文件相关联的文件，例如打开的MainActivity.java，那么他的相关文件就会是activity_main.xml ,你的同事绝对不知道这一点😜</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+H</strong></td><td style="text-align:left"><strong>可以查看当前类(只有在java文件中有效)的继承关系，你会发现不出意外的话Alt+8 与其效果是同样的</strong></td></tr><tr><td style="text-align:left">Ctrl+数字加号</td><td style="text-align:left">展开光标所在处已折叠的代码块</td></tr><tr><td style="text-align:left">Ctrl+数字减号</td><td style="text-align:left">折叠光标所在处已展开的代码块</td></tr><tr><td style="text-align:left">Ctrl+Shift+数字加号</td><td style="text-align:left">展开所有已折叠的代码块</td></tr><tr><td style="text-align:left">Ctrl+Shift+数字减号</td><td style="text-align:left">折叠所有已展开的代码块</td></tr><tr><td style="text-align:left">Ctrl+空格</td><td style="text-align:left">代码提示，但是Windows系统下好像是输入法的切换，如果你还是想使用这个快捷键，要么将其改为Ctrl+3，要么就修改注册表咯。<a href="http://blog.csdn.net/u014284252/article/details/41854969" target="_blank" rel="noopener">方法1</a>，<a href="http://blog.csdn.net/u014284252/article/details/41854969" target="_blank" rel="noopener">方法2</a></td></tr><tr><td style="text-align:left"><strong>Alt+Enter</strong></td><td style="text-align:left"><strong>绝对是用的最多的，可以自动补全当行并且将光标跳到下一行，当新引入一个类但是还没有导入的时候，只需要对着类的名字使用该快捷键就可以自动导入了</strong></td></tr><tr><td style="text-align:left"><strong>Shift+Enter</strong></td><td style="text-align:left"><strong>直接从当前行跳转到下一行，不用将光标置于当前行末尾然后再按回车，十分方便</strong></td></tr><tr><td style="text-align:left">Alt+/</td><td style="text-align:left">同样也是代码提示，不过这个提示只是提供在文件中使用过的单词，向上循环</td></tr><tr><td style="text-align:left">Alt+Shift+/</td><td style="text-align:left">还是代码提示，提示只提供在文件中使用过的单词，向下循环</td></tr><tr><td style="text-align:left"><strong>Ctrl+/</strong></td><td style="text-align:left"><strong>代码注释，适用于单行，也可以选择多行，用得最多的了，不用我强调的啦</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Shift+/</strong></td><td style="text-align:left"><strong>块注释</strong></td></tr><tr><td style="text-align:left"><strong>Alt+Insert</strong></td><td style="text-align:left"><strong>呼出Generate命令，强烈安利😎</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+J</strong></td><td style="text-align:left"><strong>插入代码模板，或者如果你记得代码模板的名称，例如fbc，接着是用Ctrl+J，则可以直接插入() findViewById(R.id.)</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+J</strong></td><td style="text-align:left"><strong>当选择一个单词之后使用该快捷键可以直接呼出模板，选择其中你需要的就行了</strong></td></tr><tr><td style="text-align:left">Ctrl+Shift+向上箭头/向下箭头</td><td style="text-align:left">在作用域的范围内上/下移一条或多条语句，如果移动的是代码块，则整个代码块将会一起移动到下一个语法正确的位置</td></tr><tr><td style="text-align:left">Alt+Shift+向上箭头/向下箭头</td><td style="text-align:left">将一条或多条语句向上/下移动而不考虑作用范围以及语法是否正确</td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+I</strong></td><td style="text-align:left"><strong>根据Code Style 中设置的方案，对当前鼠标所在的行或选中的多行进行缩进，保持代码美观很有效</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+O</strong></td><td style="text-align:left"><strong>组织导入语句</strong></td></tr><tr><td style="text-align:left">Ctrl+Alt+L</td><td style="text-align:left">对文件的代码格式进行重新的整理</td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+T</strong></td><td style="text-align:left"><strong>选中需要包裹的语句，使用该快捷键可以快速的将其被try catch，if/else，for或者for each 等包裹，用熟了会很方便</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Shift+Delete</strong></td><td style="text-align:left"><strong>选中不想被包裹的语句，使用改快捷键可以删除try catch，if/else，for或者for each等代码块</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+Shift+T</strong></td><td style="text-align:left"><strong>呼出Refactor This对话框，可以做很多有关重构的事情，四个快捷键可以记住成CAST</strong></td></tr><tr><td style="text-align:left"><strong>Shift+F6</strong></td><td style="text-align:left"><strong>重命名该文件</strong></td></tr><tr><td style="text-align:left">Ctrl+F6</td><td style="text-align:left">修改方法名以及参数</td></tr><tr><td style="text-align:left">Ctrl+Shift+F6</td><td style="text-align:left">类型迁移</td></tr><tr><td style="text-align:left"><strong>F6</strong></td><td style="text-align:left"><strong>移动文件或者移动变量到另一个文件</strong></td></tr><tr><td style="text-align:left"><strong>F5</strong></td><td style="text-align:left"><strong>复制文件或者复制变量到另一个文件</strong></td></tr><tr><td style="text-align:left"><strong>Home</strong></td><td style="text-align:left"><strong>将光标置于当前行的首个字母前</strong></td></tr><tr><td style="text-align:left"><strong>End</strong></td><td style="text-align:left"><strong>将光标置于当前行的末尾</strong></td></tr><tr><td style="text-align:left">Ctrl+Alt+V</td><td style="text-align:left">抽取变量(variable)</td></tr><tr><td style="text-align:left">Ctrl+Alt+C</td><td style="text-align:left">抽取常量(constant)</td></tr><tr><td style="text-align:left">Ctrl+Alt+F</td><td style="text-align:left">抽取字段(filed)</td></tr><tr><td style="text-align:left">Ctrl+Alt+P</td><td style="text-align:left">抽取参数(parameter)</td></tr><tr><td style="text-align:left">Ctrl+Alt+M</td><td style="text-align:left">抽取方法(Method)</td></tr><tr><td style="text-align:left"><strong>Ctrl+K</strong></td><td style="text-align:left"><strong>Git提交修改</strong></td></tr><tr><td style="text-align:left"><strong>Shift+F10</strong></td><td style="text-align:left"><strong>运行项目</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+S</strong></td><td style="text-align:left"><strong>设置</strong></td></tr><tr><td style="text-align:left"><strong>Ctrl+Alt+Shift+S</strong></td><td style="text-align:left"><strong>项目结构设置</strong></td></tr></tbody></table><p><strong>如果你有其他经常使用的快捷键而我并没有介绍到的，欢迎你留言补充，大家一起共同进步！</strong></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Android Studio2014年已经正式发布了，但是前几天才更新到2.3的版本，据说很多人升级了之后出现很多不可预见的bug，我还是乖乖守好我的2.2.3，之篇文章我和大家一起分享在使用Android Studio过程中经常会用到的以及许多能够提高效率但是我个人不经常用的快捷键，需要注意的是我只介绍Windows操作环境下的快捷键，理论上来说Windows与Linux用户在快捷键上是相同的，但是Mac用户就会比较特殊，毕竟有钱人都比较特殊对吧😜，我一个穷屌丝就暂时下不了微软大哥的船了。对了，我还会用粗体的形式标出经常会使用到的快捷键，很多时候你会觉得背这些快捷键确实是一件很烦人的事情，但是最好是实践出真知，不要硬性背，或许能够有更好的效果。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Android Studio" scheme="http://www.wensibo.top/tags/Android-Studio/"/>
    
  </entry>
  
  <entry>
    <title>写一个小便签</title>
    <link href="http://www.wensibo.top/2017/03/08/%E5%86%99%E4%B8%80%E4%B8%AA%E5%B0%8F%E4%BE%BF%E7%AD%BE/"/>
    <id>http://www.wensibo.top/2017/03/08/写一个小便签/</id>
    <published>2017-03-08T05:07:25.000Z</published>
    <updated>2018-10-21T08:26:21.188Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>欢迎访问我的<a href="http://www.wensibo.top">个人博客</a>转发请注明出处：<a href="http://www.wensibo.top/2017/03/08/写一个小便签/">http://www.wensibo.top/2017/03/08/写一个小便签/</a> </strong><br>一直想要写一个便签应用，因为我一直在用的是锤子便签和一加便签，觉得体验还是可以的，但是始终觉得自己也是可以做的，这段时间因为有些事情耽误了，项目前几天做好了，一直没有时间上传到<a href="https://github.com/Wensibob/Note" target="_blank" rel="noopener">github</a> (各位大爷路过给个star呗😉) ，今天趁着有时间顺便写了这篇文章，介绍一下写这个便签时遇到的一些问题，当作是跟大家一起分享吧！<br><a id="more"></a></p><h2 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h2><ul><li>实现最基本的增加、删除、修改便签</li><li>便签能够保存到本地</li><li>主界面采用Material Design设计风格，相对美观(勿喷)</li><li>RecyclerView上下滑动可以自动隐藏Toolbar,以及Floating Action Button</li><li>RecyclerView的Item可以实现如QQ的侧滑效果，可以通过点击删除、置顶进行编辑</li><li>可以设置便签为星✨，那么将会在便签界面左边增加一个红色的标志，以提醒用户此便签为重要便签</li><li>变迁主界面标有时间，并且按照编辑时间从新到旧进行排列</li></ul><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-8Note/1.gif" alt="效果图" title="">                </div>                <div class="image-caption">效果图</div>            </figure><p><strong>Talk is cheap,show me your code</strong></p><h2 id="如何在RecyclerView中使用本地Sqlite数据库数据"><a href="#如何在RecyclerView中使用本地Sqlite数据库数据" class="headerlink" title="如何在RecyclerView中使用本地Sqlite数据库数据"></a>如何在RecyclerView中使用本地Sqlite数据库数据</h2><p>RecyclerView是google在推出Material Design时着重介绍的一个组件，它对传统的ListView已经可以说是完全代替了，功能强大是他的一个最大优点，但是有一点局限的就是我们自定义的RecyclerView必须继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder，虽然在里面我们可以随意重写方法，但是可以发现如果我们使用数据库作为数据源，RecyclerView.Adapter是无法支持读取Cursor的，但是开源的力量又再次显现了，直接给上<a href="https://github.com/flzyup/ExRecyclerViewLibrary" target="_blank" rel="noopener">github地址</a>，但是我们这里只需要复用其中的两个文件就行了，容我娓娓道来。</p><p><strong>1、添加下面的RecyclerViewCursorAdapter 和 CursorFilter到工程中</strong><br>由于代码太长影响排版，我就直接附上下载链接<br><a href="http://www.wensibo.top/file/img/2017-3-8Note/RecyclerViewCursorAdapter.java">RecyclerViewCursorAdapter</a><br><a href="http://www.wensibo.top/file/img/2017-3-8Note/CursorFilter.java">CursorFilter</a></p><p><strong>2、新建自定义的Adapter并且继承RecyclerViewCursorAdapter</strong>    </p><ul><li>NoteAdapter     </li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NoteAdapter</span> <span class="keyword">extends</span> <span class="title">RecyclerViewCursorAdapter</span>&lt;<span class="title">NoteAdapter</span>.<span class="title">MyNoteViewHolder</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Context mContext;</span><br><span class="line">    <span class="keyword">private</span> RecyclerViewOnItemClickListener mOnItemClickListener;</span><br><span class="line">    <span class="keyword">private</span> onSwipeListener mOnSwipeListener;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">NoteAdapter</span><span class="params">(Context context,Cursor cursor,<span class="keyword">int</span> flags)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context,cursor,flags);</span><br><span class="line">        <span class="keyword">this</span>.mContext = context;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> MyNoteViewHolder <span class="title">onCreateViewHolder</span><span class="params">(ViewGroup parent, <span class="keyword">int</span> viewType)</span> </span>&#123;</span><br><span class="line">        View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_row, parent, <span class="keyword">false</span>);</span><br><span class="line">        MyNoteViewHolder holder = <span class="keyword">new</span> MyNoteViewHolder(root);</span><br><span class="line">        <span class="keyword">return</span> holder;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onBindViewHolder</span><span class="params">(<span class="keyword">final</span> MyNoteViewHolder holder, Cursor cursor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> position = cursor.getPosition();</span><br><span class="line">        holder.tv.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_CONTENT)));</span><br><span class="line">        holder.tv_dateTime.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_DATETIME)));</span><br><span class="line">        holder.mRowtab.setBackgroundColor(cursor.getInt(cursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)) == <span class="number">1</span>?</span><br><span class="line">                mContext.getResources().getColor(R.color.colorAccent):mContext.getResources().getColor(android.R.color.white)</span><br><span class="line">        );</span><br><span class="line">        holder.root.setTag(position);</span><br><span class="line"></span><br><span class="line">        holder.tv.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (mOnItemClickListener != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    mOnItemClickListener.onItemClickListener(view, holder.getAdapterPosition());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onContentChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 设置点击事件 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setRecyclerViewOnItemClickListener</span><span class="params">(RecyclerViewOnItemClickListener onItemClickListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mOnItemClickListener = onItemClickListener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RecyclerViewOnItemClickListener <span class="title">getOnItemClickListener</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mOnItemClickListener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 点击事件接口 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">RecyclerViewOnItemClickListener</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onItemClickListener</span><span class="params">(View view, <span class="keyword">int</span> position)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 内部类Holder</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">MyNoteViewHolder</span> <span class="keyword">extends</span> <span class="title">RecyclerView</span>.<span class="title">ViewHolder</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> TextView tv;</span><br><span class="line">        <span class="keyword">private</span> TextView tv_dateTime;</span><br><span class="line">        <span class="keyword">private</span> View mRowtab;</span><br><span class="line">        <span class="keyword">private</span> Button btnTop;</span><br><span class="line">        <span class="keyword">private</span> Button btnDelete;</span><br><span class="line">        <span class="keyword">private</span> View root;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">MyNoteViewHolder</span><span class="params">(View root)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">super</span>(root);</span><br><span class="line">            <span class="keyword">this</span>.root = root;</span><br><span class="line">            tv = (TextView) root.findViewById(R.id.row_text);</span><br><span class="line">            tv_dateTime = (TextView) root.findViewById(R.id.tv_note_time);</span><br><span class="line">            mRowtab = root.findViewById(R.id.row_tab);</span><br><span class="line">            btnTop = (Button) root.findViewById(R.id.btnTop);</span><br><span class="line">            btnDelete = (Button) root.findViewById(R.id.btnDelete);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3、在Activity中这样用</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">mRecyclerView = (RecyclerView) findViewById(R.id.recycle_notes);</span><br><span class="line">        mRecyclerView.setLayoutManager(<span class="keyword">new</span> LinearLayoutManager(<span class="keyword">this</span>));</span><br><span class="line">        mCursor = mNoteDbAdapter.fetchAllNotes();</span><br><span class="line">        mNoteAdapter = <span class="keyword">new</span> NoteAdapter(<span class="keyword">this</span>, mCursor, <span class="number">0</span>);</span><br><span class="line">        Log.d(TAG, <span class="string">"mCursor的大小为："</span> + mCursor.getCount());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置点击事件</span></span><br><span class="line">        mNoteAdapter.setRecyclerViewOnItemClickListener(<span class="keyword">new</span> NoteAdapter.RecyclerViewOnItemClickListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onItemClickListener</span><span class="params">(View view, <span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (mCursor == <span class="keyword">null</span> || mCursor.isClosed()) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mCursor == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        Log.d(<span class="string">"NoteActivity"</span>, <span class="string">"newCursor is null"</span>);</span><br><span class="line">                        Toast.makeText(NoteActivity.<span class="keyword">this</span>, <span class="string">"newCursor is null"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mCursor.isClosed())&#123;</span><br><span class="line">                        Log.d(<span class="string">"NoteActivity"</span>, <span class="string">"newCursor is closed"</span>);</span><br><span class="line">                        Toast.makeText(NoteActivity.<span class="keyword">this</span>, <span class="string">"newCursor is null"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    mCursor.moveToPosition(position);</span><br><span class="line">                    String content = mCursor.getString(mCursor.getColumnIndex(NoteDbAdapter.COL_CONTENT));</span><br><span class="line">                    <span class="keyword">int</span> importtant = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT));</span><br><span class="line">                    <span class="keyword">int</span> id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));</span><br><span class="line">                    Log.d(<span class="string">"NoteActivity"</span>, content + importtant);</span><br><span class="line">                    Note clickNote = <span class="keyword">new</span> Note(id, content, importtant);</span><br><span class="line">                    Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">                    intent.setClass(NoteActivity.<span class="keyword">this</span>, NoteContentActivity.class);</span><br><span class="line">                    Bundle bundle = <span class="keyword">new</span> Bundle();</span><br><span class="line">                    bundle.putSerializable(<span class="string">"note"</span>, clickNote);</span><br><span class="line">                    intent.putExtras(bundle);</span><br><span class="line">                    startActivity(intent);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置适配器</span></span><br><span class="line">        mRecyclerView.setAdapter(mNoteAdapter);</span><br></pre></td></tr></table></figure></p><h2 id="如何在RecyclerView上下滑动时隐藏Toolbar和FAB按钮"><a href="#如何在RecyclerView上下滑动时隐藏Toolbar和FAB按钮" class="headerlink" title="如何在RecyclerView上下滑动时隐藏Toolbar和FAB按钮"></a>如何在RecyclerView上下滑动时隐藏Toolbar和FAB按钮</h2><p>思路很简单，只需要记录RecyclerView向下滑动(手指向上滑动)时移动的距离，超过一定范围时就会调用Toolbar以及Floating Action Button的animate().translationY方法，令其在Y轴方向上移动，当RecyclerView向上滑动(手指向下滑动)时又会反过来回到初始状态，并且当滑动到RecyclerView底部时会强制Toolbar和FAB回到初始状态，上代码。     </p><ul><li>HidingScrollListener</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">HidingScrollListener</span> <span class="keyword">extends</span> <span class="title">RecyclerView</span>.<span class="title">OnScrollListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> HIDE_THRESHOLD = <span class="number">20</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> scrolledDistance = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> controlsVisible = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> mItemSize=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">HidingScrollListener</span><span class="params">(<span class="keyword">int</span> itemSize)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mItemSize = itemSize - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> recyclerView</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dx 横向的滚动距离</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dy 纵向的滚动距离</span></span><br><span class="line"><span class="comment">     *           记录的是两个滚动事件之间的偏移量，而不是总的滚动距离。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onScrolled</span><span class="params">(RecyclerView recyclerView, <span class="keyword">int</span> dx, <span class="keyword">int</span> dy)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onScrolled(recyclerView, dx, dy);</span><br><span class="line">        <span class="keyword">int</span> firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();</span><br><span class="line">        <span class="keyword">int</span> lastVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (firstVisibleItem == <span class="number">0</span>||lastVisibleItem==mItemSize) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!controlsVisible) &#123;</span><br><span class="line">                onShow();</span><br><span class="line">                controlsVisible = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//如果总的滚动距离超多了一定值</span></span><br><span class="line">            <span class="comment">// （这个值取决于你自己的设定，越大，需要滑动的距离越长才能显示或者隐藏），</span></span><br><span class="line">            <span class="comment">// 我们就根据其方向显示或者隐藏Toolbar（dy&gt;0意味着下滚，dy&lt;0意味着上滚）。</span></span><br><span class="line">            <span class="keyword">if</span> (scrolledDistance &gt; HIDE_THRESHOLD &amp;&amp; controlsVisible) &#123;</span><br><span class="line">                onHide();</span><br><span class="line">                controlsVisible = <span class="keyword">false</span>;</span><br><span class="line">                scrolledDistance = <span class="number">0</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (scrolledDistance &lt; -HIDE_THRESHOLD &amp;&amp; !controlsVisible) &#123;</span><br><span class="line">                onShow();</span><br><span class="line">                scrolledDistance = <span class="number">0</span>;</span><br><span class="line">                controlsVisible = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//计算出滚动的总距离（deltas相加），</span></span><br><span class="line">        <span class="comment">// 但是只在Toolbar隐藏且上滚或者Toolbar未隐藏且下滚的时候</span></span><br><span class="line">        <span class="keyword">if</span> ((controlsVisible &amp;&amp; dy &gt; <span class="number">0</span>) || (!controlsVisible &amp;&amp; dy &lt; <span class="number">0</span>)) &#123;</span><br><span class="line">            scrolledDistance += dy;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">onHide</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">onShow</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在Avtivity中使用回掉方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//为recycleview设置滚动监听器</span></span><br><span class="line">        mRecyclerView.setOnScrollListener(<span class="keyword">new</span> HidingScrollListener(mCursor.getCount()) &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onHide</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                hideView();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onShow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                showView();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">       <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">hideView</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mToolbar.animate().translationY(</span><br><span class="line">                -mToolbar.getHeight()).setInterpolator(<span class="keyword">new</span> AccelerateInterpolator(<span class="number">2</span>));</span><br><span class="line">        FrameLayout.LayoutParams ip = (FrameLayout.LayoutParams) mFloatingActionButton.getLayoutParams();</span><br><span class="line">        <span class="keyword">int</span> fabButtonMargin = ip.bottomMargin;</span><br><span class="line">        mFloatingActionButton.animate().translationY(</span><br><span class="line">                mFloatingActionButton.getHeight() + fabButtonMargin).setInterpolator(<span class="keyword">new</span> AccelerateInterpolator(<span class="number">2</span>)).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">showView</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mToolbar.animate().translationY(<span class="number">0</span>).setInterpolator(<span class="keyword">new</span> DecelerateInterpolator(<span class="number">2</span>));</span><br><span class="line">        mFloatingActionButton.animate().translationY(<span class="number">0</span>).setInterpolator(<span class="keyword">new</span> DecelerateInterpolator(<span class="number">2</span>)).start();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p><strong>特别注意布局文件</strong><br>如果你发现你运行的效果像下面的截图一样的话，那你肯定是因为布局文件上少写了这两句<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">android:clipToPadding=&quot;false&quot;</span><br><span class="line">android:paddingTop=&quot;?attr/actionBarSize&quot;</span><br></pre></td></tr></table></figure></p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-3-8Note/2.png" alt="bug截图" title="">                </div>                <div class="image-caption">bug截图</div>            </figure>    <p>完整的布局代码如下：   </p><ul><li>main_activity.xml</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">FrameLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></span><br><span class="line"><span class="tag">             <span class="attr">xmlns:app</span>=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">android.support.v7.widget.RecyclerView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/recycle_notes"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:clipToPadding</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:paddingTop</span>=<span class="string">"?attr/actionBarSize"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">android.support.v7.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/toolbar"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"?attr/actionBarSize"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:background</span>=<span class="string">"?attr/colorPrimary"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:clipToPadding</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:titleTextColor</span>=<span class="string">"@android:color/white"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">android.support.design.widget.FloatingActionButton</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/button_add_note"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_gravity</span>=<span class="string">"bottom|right"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_marginBottom</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_marginRight</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:src</span>=<span class="string">"@drawable/ic_action_new"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:elevation</span>=<span class="string">"15dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:fabSize</span>=<span class="string">"normal"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:pressedTranslationZ</span>=<span class="string">"8dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:rippleColor</span>=<span class="string">"#ff87eb"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="最后来讲讲如何实现仿QQ的侧滑出现删除、指定操作"><a href="#最后来讲讲如何实现仿QQ的侧滑出现删除、指定操作" class="headerlink" title="最后来讲讲如何实现仿QQ的侧滑出现删除、指定操作"></a>最后来讲讲如何实现仿QQ的侧滑出现删除、指定操作</h2><p>首先得谢谢<a href="http://blog.csdn.net/zxt0601/article/details/53157090" target="_blank" rel="noopener">张旭童</a> ,他的一个库帮我解决了这个问题，<a href="https://github.com/mcxtzhang/SwipeDelMenuLayout" target="_blank" rel="noopener">点击这里</a>可以访问他的项目。<br><strong>1、在布局文件中使用<code>com.mcxtzhang.swipemenulib.SwipeMenuLayout</code>布局，在ItemView后添加button表示删除置顶按钮。</strong><br><strong>2、在Adapter中设置打开侧滑菜单，并且可以设置菜单在左还是在右</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">((SwipeMenuLayout) holder.root.findViewById(R.id.swipeMenuLayout)).setIos(<span class="keyword">false</span>).setLeftSwipe(<span class="keyword">false</span>).setSwipeEnable(<span class="keyword">true</span>);</span><br></pre></td></tr></table></figure></p><p><strong>3、在Activity中设置动作事件</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">mNoteAdapter.setOnSwipeListener(<span class="keyword">new</span> NoteAdapter.onSwipeListener() &#123;</span><br><span class="line">           <span class="meta">@Override</span></span><br><span class="line">           <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDel</span><span class="params">(<span class="keyword">int</span> pos)</span> </span>&#123;</span><br><span class="line">               Toast.makeText(NoteActivity.<span class="keyword">this</span>, <span class="string">"点击了第"</span> + (pos+<span class="number">1</span>) + <span class="string">"条item的删除按钮"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">               mCursor.moveToPosition(pos);</span><br><span class="line">               <span class="keyword">int</span> id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));</span><br><span class="line">               mNoteDbAdapter.deleteNoteById(id);</span><br><span class="line">               mCursor = mNoteDbAdapter.fetchAllNotes();</span><br><span class="line">               mNoteAdapter.changeCursor(mCursor);</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           <span class="meta">@Override</span></span><br><span class="line">           <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onTop</span><span class="params">(<span class="keyword">int</span> pos)</span> </span>&#123;</span><br><span class="line">               Toast.makeText(NoteActivity.<span class="keyword">this</span>, <span class="string">"点击了第"</span> + (pos+<span class="number">1</span>) + <span class="string">"条item的Top按钮"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">               mCursor.moveToPosition(pos);</span><br><span class="line">               <span class="keyword">int</span> id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));</span><br><span class="line">               Note editNote = mNoteDbAdapter.fetchNoteById(id);</span><br><span class="line">               editNote.setDateTime(DateUtil.formatDateTime());</span><br><span class="line">               mNoteDbAdapter.updateNote(editNote);</span><br><span class="line">               mCursor = mNoteDbAdapter.fetchAllNotes();</span><br><span class="line">               mNoteAdapter.changeCursor(mCursor);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;);</span><br></pre></td></tr></table></figure></p><p><strong>大功告成，如果想要看详细代码，或者有什么建议可以到<a href="https://github.com/Wensibob/Note" target="_blank" rel="noopener">Github</a>上给我发Issue或者直接在站内给我留言哦，记得star哦</strong></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;欢迎访问我的&lt;a href=&quot;http://www.wensibo.top&quot;&gt;个人博客&lt;/a&gt;转发请注明出处：&lt;a href=&quot;http://www.wensibo.top/2017/03/08/写一个小便签/&quot;&gt;http://www.wensibo.top/2017/03/08/写一个小便签/&lt;/a&gt; &lt;/strong&gt;&lt;br&gt;一直想要写一个便签应用，因为我一直在用的是锤子便签和一加便签，觉得体验还是可以的，但是始终觉得自己也是可以做的，这段时间因为有些事情耽误了，项目前几天做好了，一直没有时间上传到&lt;a href=&quot;https://github.com/Wensibob/Note&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github&lt;/a&gt; (各位大爷路过给个star呗😉) ，今天趁着有时间顺便写了这篇文章，介绍一下写这个便签时遇到的一些问题，当作是跟大家一起分享吧！&lt;br&gt;
    
    </summary>
    
      <category term="项目开发" scheme="http://www.wensibo.top/categories/%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="Android" scheme="http://www.wensibo.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Volley（四）</title>
    <link href="http://www.wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E5%9B%9B%EF%BC%89/"/>
    <id>http://www.wensibo.top/2017/02/17/一口一口吃掉Volley（四）/</id>
    <published>2017-02-17T11:29:25.000Z</published>
    <updated>2018-10-21T08:26:21.492Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>非常感谢你能够坚持看到第四篇，同时这也是这个Volley系列教程的最后一篇了。经过前三节的学习，相信你也已经懂得如何运用Volley提供的Request以及自定义Request了，这一节我将从源码的角度带领大家理解Volley的工作流程。<br><a id="more"></a></p><h2 id="从newRequestQueue-看起"><a href="#从newRequestQueue-看起" class="headerlink" title="从newRequestQueue()看起"></a>从newRequestQueue()看起</h2><p>我们都知道，使用Volley最开始要做的就是使用<code>newRequestQueue()</code>获取一个RequestQueue对象，仔细看一下这个方法</p><ul><li>newRequestQueue()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> RequestQueue <span class="title">newRequestQueue</span><span class="params">(Context context, HttpStack stack, <span class="keyword">int</span> maxDiskCacheBytes)</span> </span>&#123;</span><br><span class="line">        File cacheDir = <span class="keyword">new</span> File(context.getCacheDir(), DEFAULT_CACHE_DIR);</span><br><span class="line"></span><br><span class="line">        String userAgent = <span class="string">"volley/0"</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            String packageName = context.getPackageName();</span><br><span class="line">            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, <span class="number">0</span>);</span><br><span class="line">            userAgent = packageName + <span class="string">"/"</span> + info.versionCode;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NameNotFoundException e) &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (stack == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= <span class="number">9</span>) &#123;</span><br><span class="line">                stack = <span class="keyword">new</span> HurlStack();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Prior to Gingerbread, HttpUrlConnection was unreliable.</span></span><br><span class="line">                <span class="comment">// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html</span></span><br><span class="line">                stack = <span class="keyword">new</span> HttpClientStack(AndroidHttpClient.newInstance(userAgent));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Network network = <span class="keyword">new</span> BasicNetwork(stack);</span><br><span class="line">        </span><br><span class="line">        RequestQueue queue;</span><br><span class="line">        <span class="keyword">if</span> (maxDiskCacheBytes &lt;= -<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// No maximum size specified</span></span><br><span class="line">            queue = <span class="keyword">new</span> RequestQueue(<span class="keyword">new</span> DiskBasedCache(cacheDir), network);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// Disk cache size specified</span></span><br><span class="line">            queue = <span class="keyword">new</span> RequestQueue(<span class="keyword">new</span> DiskBasedCache(cacheDir, maxDiskCacheBytes), network);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        queue.start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> queue;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>在方法内部我们可以看到在api等级大于9的时候，使用HurlStack实例来进行主要的网络请求工作，到这里已经很明显了，Volley底层是使用HttpUrlConnection进行的；而对于小于9的API则创建否则就创建一个HttpClientStack的实例，也就是对于9之前的API使用HttpClient进行网络通讯。最后被包装为一个BasicNetwork对象。<br>接着根据得到的BasicNetwork对象和一个DiskBasedCache对象（磁盘缓存）来构造一个RequestQueue，并且调用了它的start方法来启动这个线程。</p></blockquote><h2 id="接着看start"><a href="#接着看start" class="headerlink" title="接着看start()"></a>接着看start()</h2><ul><li>start()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stop();  <span class="comment">// Make sure any currently running dispatchers are stopped.</span></span><br><span class="line">        <span class="comment">// Create the cache dispatcher and start it.</span></span><br><span class="line">        mCacheDispatcher = <span class="keyword">new</span> CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);</span><br><span class="line">        mCacheDispatcher.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Create network dispatchers (and corresponding threads) up to the pool size.</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; mDispatchers.length; i++) &#123;</span><br><span class="line">            NetworkDispatcher networkDispatcher = <span class="keyword">new</span> NetworkDispatcher(mNetworkQueue, mNetwork,</span><br><span class="line">                    mCache, mDelivery);</span><br><span class="line">            mDispatchers[i] = networkDispatcher;</span><br><span class="line">            networkDispatcher.start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>首先先创建CacheDispatcher对象，接着进入for循环这个for循环遍历了mCacheDispatcher，这个mCacheDispatcher其实相当于一个线程池，这个线程池的大小默认是4。然后分别让这里面的线程运行起来（调用了它们的start方法）。这里为什么要有多个线程来处理呢？原因很简单，因为我们每一个请求都不一定会马上处理完毕，多个线程进行同时处理的话效率会提高。 所以最终这里会有5个线程，4个是网络线程NetworkDispatcher，1个是缓存线程CacheDispatcher。</p></blockquote><p>得到了RequestQueue之后，我们只需要构建出相应的Request，然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了，那就先来看一下add()吧！</p><h2 id="add-方法"><a href="#add-方法" class="headerlink" title="add()方法"></a>add()方法</h2><ul><li>add()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; <span class="function">Request&lt;T&gt; <span class="title">add</span><span class="params">(Request&lt;T&gt; request)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Tag the request as belonging to this queue and add it to the set of current requests.</span></span><br><span class="line">        request.setRequestQueue(<span class="keyword">this</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (mCurrentRequests) &#123;</span><br><span class="line">            mCurrentRequests.add(request);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Process requests in the order they are added.</span></span><br><span class="line">        request.setSequence(getSequenceNumber());</span><br><span class="line">        request.addMarker(<span class="string">"add-to-queue"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// If the request is uncacheable, skip the cache queue and go straight to the network.</span></span><br><span class="line">        <span class="keyword">if</span> (!request.shouldCache()) &#123;</span><br><span class="line">            mNetworkQueue.add(request);</span><br><span class="line">            <span class="keyword">return</span> request;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Insert request into stage if there's already a request with the same cache key in flight.</span></span><br><span class="line">        <span class="keyword">synchronized</span> (mWaitingRequests) &#123;</span><br><span class="line">            String cacheKey = request.getCacheKey();</span><br><span class="line">            <span class="keyword">if</span> (mWaitingRequests.containsKey(cacheKey)) &#123;</span><br><span class="line">                <span class="comment">// There is already a request in flight. Queue up.</span></span><br><span class="line">                Queue&lt;Request&lt;?&gt;&gt; stagedRequests = mWaitingRequests.get(cacheKey);</span><br><span class="line">                <span class="keyword">if</span> (stagedRequests == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    stagedRequests = <span class="keyword">new</span> LinkedList&lt;Request&lt;?&gt;&gt;();</span><br><span class="line">                &#125;</span><br><span class="line">                stagedRequests.add(request);</span><br><span class="line">                mWaitingRequests.put(cacheKey, stagedRequests);</span><br><span class="line">                <span class="keyword">if</span> (VolleyLog.DEBUG) &#123;</span><br><span class="line">                    VolleyLog.v(<span class="string">"Request for cacheKey=%s is in flight, putting on hold."</span>, cacheKey);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Insert 'null' queue for this cacheKey, indicating there is now a request in</span></span><br><span class="line">                <span class="comment">// flight.</span></span><br><span class="line">                mWaitingRequests.put(cacheKey, <span class="keyword">null</span>);</span><br><span class="line">                mCacheQueue.add(request);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> request;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>可以看到，在第13行的时候会判断当前的请求是否可以缓存，如果不能缓存则在第14行直接将这条请求加入网络请求队列，可以缓存的话则在第36行将这条请求加入缓存队列。在默认情况下，每条请求都是可以缓存的，当然我们也可以调用Request的setShouldCache(false)方法来改变这一默认行为。 </p></blockquote><h2 id="那就先来看看NetworkDispatcher的run-吧"><a href="#那就先来看看NetworkDispatcher的run-吧" class="headerlink" title="那就先来看看NetworkDispatcher的run()吧!"></a>那就先来看看NetworkDispatcher的run()吧!</h2><ul><li>run()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);</span><br><span class="line">        Request&lt;?&gt; request;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="keyword">long</span> startTimeMs = SystemClock.elapsedRealtime();</span><br><span class="line">            <span class="comment">// release previous request object to avoid leaking request object when mQueue is drained.</span></span><br><span class="line">            request = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// Take a request from the queue.</span></span><br><span class="line">                request = mQueue.take();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                <span class="comment">// We may have been interrupted because it was time to quit.</span></span><br><span class="line">                <span class="keyword">if</span> (mQuit) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                request.addMarker(<span class="string">"network-queue-take"</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// If the request was cancelled already, do not perform the</span></span><br><span class="line">                <span class="comment">// network request.</span></span><br><span class="line">                <span class="keyword">if</span> (request.isCanceled()) &#123;</span><br><span class="line">                    request.finish(<span class="string">"network-discard-cancelled"</span>);</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                addTrafficStatsTag(request);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Perform the network request.</span></span><br><span class="line">                NetworkResponse networkResponse = mNetwork.performRequest(request);</span><br><span class="line">                request.addMarker(<span class="string">"network-http-complete"</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// If the server returned 304 AND we delivered a response already,</span></span><br><span class="line">                <span class="comment">// we're done -- don't deliver a second identical response.</span></span><br><span class="line">                <span class="keyword">if</span> (networkResponse.notModified &amp;&amp; request.hasHadResponseDelivered()) &#123;</span><br><span class="line">                    request.finish(<span class="string">"not-modified"</span>);</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Parse the response here on the worker thread.</span></span><br><span class="line">                Response&lt;?&gt; response = request.parseNetworkResponse(networkResponse);</span><br><span class="line">                request.addMarker(<span class="string">"network-parse-complete"</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Write to cache if applicable.</span></span><br><span class="line">                <span class="comment">// <span class="doctag">TODO:</span> Only update cache metadata instead of entire record for 304s.</span></span><br><span class="line">                <span class="keyword">if</span> (request.shouldCache() &amp;&amp; response.cacheEntry != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    mCache.put(request.getCacheKey(), response.cacheEntry);</span><br><span class="line">                    request.addMarker(<span class="string">"network-cache-written"</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Post the response back.</span></span><br><span class="line">                request.markDelivered();</span><br><span class="line">                mDelivery.postResponse(request, response);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (VolleyError volleyError) &#123;</span><br><span class="line">                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);</span><br><span class="line">                parseAndDeliverNetworkError(request, volleyError);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                VolleyLog.e(e, <span class="string">"Unhandled exception %s"</span>, e.toString());</span><br><span class="line">                VolleyError volleyError = <span class="keyword">new</span> VolleyError(e);</span><br><span class="line">                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);</span><br><span class="line">                mDelivery.postError(request, volleyError);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>第4行设置了这些线程的优先级，这个优先级比较低，目的是为了尽量减少对UI线程的影响保证流畅度。  <br><br>接着第12行，调用mQueue的take方法取出队列头的一个请求进行处理，这个mQueue就是我们在上面add方法中添加进去的一个请求。  <br><br>直接看到第34行，如果请求没有被取消，也就是正常的情况下，我们会调用mNetwork的performRequest方法进行请求的处理。不知道你还记的这个mNetwork不，它其实就是我们上面提到的那个由HttpUrlConnection层层包装的网络请求对象。  <br><br>如果请求得到了结果，我们会看到55行调用了mDelivery的postResponose方法来回传我们的请求结果。</p></blockquote><h2 id="先来看performRequest"><a href="#先来看performRequest" class="headerlink" title="先来看performRequest()"></a>先来看performRequest()</h2><p>因为Network是一个接口，这里具体的实现是BasicNetwork，所以我们可以看到其中重写的performRequest()如下：  </p><ul><li>performRequest()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> NetworkResponse <span class="title">performRequest</span><span class="params">(Request&lt;?&gt; request)</span> <span class="keyword">throws</span> VolleyError </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> requestStart = SystemClock.elapsedRealtime();</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            HttpResponse httpResponse = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">byte</span>[] responseContents = <span class="keyword">null</span>;</span><br><span class="line">            Map&lt;String, String&gt; responseHeaders = Collections.emptyMap();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// Gather headers.</span></span><br><span class="line">                Map&lt;String, String&gt; headers = <span class="keyword">new</span> HashMap&lt;String, String&gt;();</span><br><span class="line">                addCacheHeaders(headers, request.getCacheEntry());</span><br><span class="line">                httpResponse = mHttpStack.performRequest(request, headers);</span><br><span class="line">                StatusLine statusLine = httpResponse.getStatusLine();</span><br><span class="line">                <span class="keyword">int</span> statusCode = statusLine.getStatusCode();</span><br><span class="line"></span><br><span class="line">                responseHeaders = convertHeaders(httpResponse.getAllHeaders());</span><br><span class="line">                <span class="comment">// Handle cache validation.</span></span><br><span class="line">                <span class="keyword">if</span> (statusCode == HttpStatus.SC_NOT_MODIFIED) &#123;</span><br><span class="line"></span><br><span class="line">                    Entry entry = request.getCacheEntry();</span><br><span class="line">                    <span class="keyword">if</span> (entry == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">new</span> NetworkResponse(HttpStatus.SC_NOT_MODIFIED, <span class="keyword">null</span>,</span><br><span class="line">                                responseHeaders, <span class="keyword">true</span>,</span><br><span class="line">                                SystemClock.elapsedRealtime() - requestStart);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// A HTTP 304 response does not have all header fields. We</span></span><br><span class="line">                    <span class="comment">// have to use the header fields from the cache entry plus</span></span><br><span class="line">                    <span class="comment">// the new ones from the response.</span></span><br><span class="line">                    <span class="comment">// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5</span></span><br><span class="line">                    entry.responseHeaders.putAll(responseHeaders);</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">new</span> NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,</span><br><span class="line">                            entry.responseHeaders, <span class="keyword">true</span>,</span><br><span class="line">                            SystemClock.elapsedRealtime() - requestStart);</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// Handle moved resources</span></span><br><span class="line">                <span class="keyword">if</span> (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) &#123;</span><br><span class="line">                    String newUrl = responseHeaders.get(<span class="string">"Location"</span>);</span><br><span class="line">                    request.setRedirectUrl(newUrl);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Some responses such as 204s do not have content.  We must check.</span></span><br><span class="line">                <span class="keyword">if</span> (httpResponse.getEntity() != <span class="keyword">null</span>) &#123;</span><br><span class="line">                  responseContents = entityToBytes(httpResponse.getEntity());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                  <span class="comment">// Add 0 byte response as a way of honestly representing a</span></span><br><span class="line">                  <span class="comment">// no-content request.</span></span><br><span class="line">                  responseContents = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">0</span>];</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// if the request is slow, log it.</span></span><br><span class="line">                <span class="keyword">long</span> requestLifetime = SystemClock.elapsedRealtime() - requestStart;</span><br><span class="line">                logSlowRequests(requestLifetime, request, responseContents, statusLine);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (statusCode &lt; <span class="number">200</span> || statusCode &gt; <span class="number">299</span>) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> IOException();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> NetworkResponse(statusCode, responseContents, responseHeaders, <span class="keyword">false</span>,</span><br><span class="line">                        SystemClock.elapsedRealtime() - requestStart);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                ···</span><br><span class="line">            &#125; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>这段代码中，先10和11行代码将cache的属性设置给header，接着第12行调用mHttpStack对象的performRequest方法并传入请求对象和头部来进行请求，得到一个HttpResponse对象。  <br><br>接着将HttpResponse对象中的状态码取出，如果值为HttpStatus.SC_NOT_MODIFIED（也就是304），则表示请求得到的Response没有变化，直接显示缓存内容。  <br><br>第45行表示请求成功并且获取到请求内容，将内容取出并作为一个NetworkResponse对象的属性并返回给NetworkDispatcher。  <br><br>在NetworkDispatcher中收到了NetworkResponse这个返回值后又会调用Request的parseNetworkResponse()方法来解析NetworkResponse中的数据，以及将数据写入到缓存，这个方法的实现是交给Request的子类来完成的，因为不同种类的Request解析的方式也肯定不同，这就是为什么我们在自定义Request的时候必须要重写parseNetworkResponse()这个方法的原因了。</p></blockquote><p>在解析完了NetworkResponse中的数据之后，又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据。</p><h2 id="接着是postResponse"><a href="#接着是postResponse" class="headerlink" title="接着是postResponse()"></a>接着是postResponse()</h2><ul><li>postResponse()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postResponse</span><span class="params">(Request&lt;?&gt; request, Response&lt;?&gt; response, Runnable runnable)</span> </span>&#123;</span><br><span class="line">        request.markDelivered();</span><br><span class="line">        request.addMarker(<span class="string">"post-response"</span>);</span><br><span class="line">        mResponsePoster.execute(<span class="keyword">new</span> ResponseDeliveryRunnable(request, response, runnable));</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>这里看到第5行调用了mResponsePoster的execute方法并传入了一个ResponseDeliveryRunnable对象，再看mResponsePoster的定义：  </p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ExecutorDelivery</span><span class="params">(<span class="keyword">final</span> Handler handler)</span> </span>&#123;</span><br><span class="line">       <span class="comment">// Make an Executor that just wraps the handler.</span></span><br><span class="line">       mResponsePoster = <span class="keyword">new</span> Executor() &#123;</span><br><span class="line">           <span class="meta">@Override</span></span><br><span class="line">           <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span> </span>&#123;</span><br><span class="line">               handler.post(command);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><blockquote><p>也就是我们在这里把ResponseDeliveryRunnable对象通过Handler的post方法发送出去了。这里为什么要发送到MainLooper中？因为RequestQueue是在子线程中执行的，回调到的代码也是在子线程中的，如果在回调中修改UI，就会报错。再者，为什么要使用post方法？原因也很简单，因为我们在消息发出之后再进行回调，post方法允许我们传入一个Runnable的实现类，post成功会自动执行它的run方法，这个时候在run方法中进行结果的判断并且进行回调： </p></blockquote><ul><li>run()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">     <span class="comment">// If this request has canceled, finish it and don't deliver.</span></span><br><span class="line">     <span class="keyword">if</span> (mRequest.isCanceled()) &#123;</span><br><span class="line">         mRequest.finish(<span class="string">"canceled-at-delivery"</span>);</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// Deliver a normal response or error, depending.</span></span><br><span class="line">     <span class="keyword">if</span> (mResponse.isSuccess()) &#123;</span><br><span class="line">         mRequest.deliverResponse(mResponse.result);</span><br><span class="line">     &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         mRequest.deliverError(mResponse.error);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// If this is an intermediate response, add a marker, otherwise we're done</span></span><br><span class="line">     <span class="comment">// and the request can be finished.</span></span><br><span class="line">     <span class="keyword">if</span> (mResponse.intermediate) &#123;</span><br><span class="line">         mRequest.addMarker(<span class="string">"intermediate-response"</span>);</span><br><span class="line">     &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">         mRequest.finish(<span class="string">"done"</span>);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// If we have been provided a post-delivery runnable, run it.</span></span><br><span class="line">     <span class="keyword">if</span> (mRunnable != <span class="keyword">null</span>) &#123;</span><br><span class="line">         mRunnable.run();</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>可以看到，11行是调用Request的deleverResponse方法将结果回调给Request。举例看一下StringRequest中该方法是如何实现的：  </p></blockquote><ul><li>deliverResponse()  </li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">deliverResponse</span><span class="params">(String response)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mListener != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mListener.onResponse(response);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>直接通过我们构造StringRequest时传进来的Listener的回调方法onResponse来将结果回调给Activity。deleverError也是同样的做法。</p></blockquote><hr><p><strong>看完网络线程NetworkDispatcher之后再来看一下缓存线程CacheDispatcher是如何工作的</strong></p><hr><h2 id="最后来看CacheDispatcher的run-方法"><a href="#最后来看CacheDispatcher的run-方法" class="headerlink" title="最后来看CacheDispatcher的run()方法"></a>最后来看CacheDispatcher的run()方法</h2><ul><li>run()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG) VolleyLog.v(<span class="string">"start new dispatcher"</span>);</span><br><span class="line">    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Make a blocking call to initialize the cache.</span></span><br><span class="line">    mCache.initialize();</span><br><span class="line"></span><br><span class="line">    Request&lt;?&gt; request;</span><br><span class="line">    <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">        <span class="comment">// release previous request object to avoid leaking request object when mQueue is drained.</span></span><br><span class="line">        request = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// Take a request from the queue.</span></span><br><span class="line">            request = mCacheQueue.take();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            <span class="comment">// We may have been interrupted because it was time to quit.</span></span><br><span class="line">            <span class="keyword">if</span> (mQuit) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            request.addMarker(<span class="string">"cache-queue-take"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If the request has been canceled, don't bother dispatching it.</span></span><br><span class="line">            <span class="keyword">if</span> (request.isCanceled()) &#123;</span><br><span class="line">                request.finish(<span class="string">"cache-discard-canceled"</span>);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Attempt to retrieve this item from cache.</span></span><br><span class="line">            Cache.Entry entry = mCache.get(request.getCacheKey());</span><br><span class="line">            <span class="keyword">if</span> (entry == <span class="keyword">null</span>) &#123;</span><br><span class="line">                request.addMarker(<span class="string">"cache-miss"</span>);</span><br><span class="line">                <span class="comment">// Cache miss; send off to the network dispatcher.</span></span><br><span class="line">                mNetworkQueue.put(request);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If it is completely expired, just send it to the network.</span></span><br><span class="line">            <span class="keyword">if</span> (entry.isExpired()) &#123;</span><br><span class="line">                request.addMarker(<span class="string">"cache-hit-expired"</span>);</span><br><span class="line">                request.setCacheEntry(entry);</span><br><span class="line">                mNetworkQueue.put(request);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// We have a cache hit; parse its data for delivery back to the request.</span></span><br><span class="line">            request.addMarker(<span class="string">"cache-hit"</span>);</span><br><span class="line">            Response&lt;?&gt; response = request.parseNetworkResponse(</span><br><span class="line">                    <span class="keyword">new</span> NetworkResponse(entry.data, entry.responseHeaders));</span><br><span class="line">            request.addMarker(<span class="string">"cache-hit-parsed"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!entry.refreshNeeded()) &#123;</span><br><span class="line">                <span class="comment">// Completely unexpired cache hit. Just deliver the response.</span></span><br><span class="line">                mDelivery.postResponse(request, response);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Soft-expired cache hit. We can deliver the cached response,</span></span><br><span class="line">                <span class="comment">// but we need to also send the request to the network for</span></span><br><span class="line">                <span class="comment">// refreshing.</span></span><br><span class="line">                request.addMarker(<span class="string">"cache-hit-refresh-needed"</span>);</span><br><span class="line">                request.setCacheEntry(entry);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Mark the response as intermediate.</span></span><br><span class="line">                response.intermediate = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Post the intermediate response back to the user and have</span></span><br><span class="line">                <span class="comment">// the delivery then forward the request along to the network.</span></span><br><span class="line">                <span class="keyword">final</span> Request&lt;?&gt; finalRequest = request;</span><br><span class="line">                mDelivery.postResponse(request, response, <span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            mNetworkQueue.put(finalRequest);</span><br><span class="line">                        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                            <span class="comment">// Not much we can do about this.</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            VolleyLog.e(e, <span class="string">"Unhandled exception %s"</span>, e.toString());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>首先在10行可以看到一个while(true)循环，说明缓存线程始终是在运行的，<br>接着在第33行会尝试从缓存当中取出响应结果，如何为空的话则把这条请求加入到网络请求队列中，如果不为空的话再判断该缓存是否已过期，如果已经过期了则同样把这条请求加入到网络请求队列中，否则就认为不需要重发网络请求，直接使用缓存中的数据即可。  <br><br>之后会在第39行调用Request的parseNetworkResponse()方法来对数据进行解析，再往后就是将解析出来的数据进行回调了，跟上面的回掉思路是完全一样的！</p></blockquote><hr><p>至此，我们可以通过通过Volley官方提供的流程图重新回顾一下整个的流程<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/volley.png" alt="Volley流程图" title="">                </div>                <div class="image-caption">Volley流程图</div>            </figure>    </p><p>其中蓝色部分代表主线程，绿色部分代表缓存线程，橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求，这条请求会先被加入到缓存队列当中，如果发现可以找到相应的缓存结果就直接读取缓存并解析，然后回调给主线程。如果在缓存中没有找到结果，则将这条请求加入到网络请求队列中，然后处理发送HTTP请求，解析响应结果，写入缓存，并回调主线程。</p><p>希望通过这个系列的文章你能够清晰的掌握和理解Volley，尽管他现在已经不流行了，接下来我会持续为大家讲解比较好的开源框架，TX☺</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;非常感谢你能够坚持看到第四篇，同时这也是这个Volley系列教程的最后一篇了。经过前三节的学习，相信你也已经懂得如何运用Volley提供的Request以及自定义Request了，这一节我将从源码的角度带领大家理解Volley的工作流程。&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Volley" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley/"/>
    
    
      <category term="Volley" scheme="http://www.wensibo.top/tags/Volley/"/>
    
      <category term="Android" scheme="http://www.wensibo.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Volley（三）</title>
    <link href="http://www.wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%B8%89%EF%BC%89/"/>
    <id>http://www.wensibo.top/2017/02/17/一口一口吃掉Volley（三）/</id>
    <published>2017-02-17T07:23:25.000Z</published>
    <updated>2018-10-21T08:26:21.448Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>学习了<a href="http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%BA%8C%EF%BC%89/" target="_blank" rel="noopener">一口一口吃掉Volley（二）</a>之后，你应该已经学会了如何使用Volley自带的Request，但是有的时候我们需要解析的数据多种多样，例如XML又或者你想使用Google的gson，那么当Volley不能直接提供给我们这些功能的时候就需要我们进行自定义了，第一节我也向大家讲过Volley面向接口编程，使得其很容易扩展，那么这节课我们就一起来学习自定义的Request吧！<br><a id="more"></a></p><h2 id="从StringRequest开始讲讲思路"><a href="#从StringRequest开始讲讲思路" class="headerlink" title="从StringRequest开始讲讲思路"></a>从StringRequest开始讲讲思路</h2><p>先上最基本的StringRequest源码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StringRequest</span> <span class="keyword">extends</span> <span class="title">Request</span>&lt;<span class="title">String</span>&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Response.Listener&lt;String&gt; mListener;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 根据给定的METHOD设置对应的request. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">StringRequest</span><span class="params">(<span class="keyword">int</span> method, String url, Response.Listener&lt;String&gt; listener,</span></span></span><br><span class="line"><span class="function"><span class="params">                         Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(method, url, errorListener);</span><br><span class="line">        mListener = listener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 默认为GET请求的request. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">StringRequest</span><span class="params">(String url, Response.Listener&lt;String&gt; listener,</span></span></span><br><span class="line"><span class="function"><span class="params">                         Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(Method.GET, url, listener, errorListener);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 将HTTP请求结果转换为String. */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Response&lt;String&gt; <span class="title">parseNetworkResponse</span><span class="params">(NetworkResponse response)</span> </span>&#123;</span><br><span class="line">        String parsed;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            parsed = <span class="keyword">new</span> String(response.data, HttpHeaderParser.parseCharset(response.headers));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (UnsupportedEncodingException e) &#123;</span><br><span class="line">            parsed = <span class="keyword">new</span> String(response.data);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 将解析的String结果传递给用户的回调接口. */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">deliverResponse</span><span class="params">(String response)</span> </span>&#123;</span><br><span class="line">        mListener.onResponse(response);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们应该可以提炼出如下几点：</p><ul><li>StringRequest继承自Request类，并制定其泛型为String，那么当我们自定义XMLRequest时就应该指定类型为XmlPullParser。</li><li>有两个构造函数，默认使用的GET请求。</li><li>由于Request类中的deliverResponse()和parseNetworkResponse()是两个抽象方法，因此自定义Request中需要对这两个方法进行实现。</li><li>deliverResponse()方法中仅仅是调用了mListener中的onResponse()方法，并将response内容传入即可，这样就可以将服务器响应的数据进行回调。</li><li>parseNetworkResponse()方法中则是对服务器响应的数据进行解析，其中数据是以字节的形式存放在NetworkResponse的response变量中的，这里将数据取出然后组装成一个String，并传入Response的success()方法中即可。<br>既然知道内部实现的逻辑，那就开始动手吧！</li></ul><h2 id="自定义XMLRequest"><a href="#自定义XMLRequest" class="headerlink" title="自定义XMLRequest"></a>自定义XMLRequest</h2><p><strong>① 直接上代码啦！</strong><br>按照刚才我们解析StringRequest得到的几点结论，我们应该不难得到XMLRequest：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">XMLRequest</span> <span class="keyword">extends</span> <span class="title">Request</span>&lt;<span class="title">XmlPullParser</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Response.Listener&lt;XmlPullParser&gt; mListener;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">XMLRequest</span><span class="params">(<span class="keyword">int</span> method, String url, Response.Listener&lt;XmlPullParser&gt; listener, Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(method, url, errorListener);</span><br><span class="line">        mListener = listener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">XMLRequest</span><span class="params">( String url, Response.Listener&lt;XmlPullParser&gt; listener, Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(Method.GET, url, listener, errorListener);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Response&lt;XmlPullParser&gt; <span class="title">parseNetworkResponse</span><span class="params">(NetworkResponse response)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            response.headers.put(<span class="string">"HTTP.CONTENT_TYPE"</span>, <span class="string">"utf-8"</span>);</span><br><span class="line">            String xmlString = <span class="keyword">new</span> String(response.data,<span class="string">"utf-8"</span>);</span><br><span class="line">            <span class="comment">//加上这两行可以解决乱码的问题，尤其是对于中文的xml接口，由于每个xml的编码格式不同，所以获取原编码格式之后再转换为utf-8，即可。</span></span><br><span class="line">            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();</span><br><span class="line">            XmlPullParser xmlPullParser = factory.newPullParser();</span><br><span class="line">            xmlPullParser.setInput(<span class="keyword">new</span> StringReader(xmlString));</span><br><span class="line">            Log.d(<span class="string">"SUCCESS"</span>, <span class="string">"xmlString的内容为"</span> + xmlString);</span><br><span class="line">            <span class="keyword">return</span> Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (UnsupportedEncodingException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> Response.error(<span class="keyword">new</span> ParseError(e));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (XmlPullParserException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> Response.error(<span class="keyword">new</span> ParseError(e));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">deliverResponse</span><span class="params">(XmlPullParser response)</span> </span>&#123;</span><br><span class="line">        mListener.onResponse(response);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>这里需要注意的一点我在<a href="http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%BA%8C%EF%BC%89/" target="_blank" rel="noopener">上一篇</a>文章中也已经提到了，就是在解析数据的时候如果出现乱码应该转换编码为utf-8。</p></blockquote><p><strong>② 测试接口</strong><br>作为测试，我使用的XML接口是：<a href="http://flash.weather.com.cn/wmaps/xml/guangdong.xml" target="_blank" rel="noopener">http://flash.weather.com.cn/wmaps/xml/guangdong.xml</a> ，它返回的是广东省各个城市的天气预报，你也可以将<code>guangdong</code>改为你所在的省份就可以显示其他数据了：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">guangdong</span> <span class="attr">dn</span>=<span class="string">"day"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"137.7"</span> <span class="attr">cityY</span>=<span class="string">"385.95"</span> <span class="attr">cityname</span>=<span class="string">"湛江"</span> <span class="attr">centername</span>=<span class="string">"湛江"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"zhanjiang"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"23"</span> <span class="attr">tem2</span>=<span class="string">"16"</span> <span class="attr">temNow</span>=<span class="string">"24"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东南风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"50%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281001"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"170.65"</span> <span class="attr">cityY</span>=<span class="string">"317.55"</span> <span class="attr">cityname</span>=<span class="string">"茂名"</span> <span class="attr">centername</span>=<span class="string">"茂名"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"maoming"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"西南风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"47%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101282001"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"225"</span> <span class="attr">cityY</span>=<span class="string">"245"</span> <span class="attr">cityname</span>=<span class="string">"云浮"</span> <span class="attr">centername</span>=<span class="string">"云浮"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"yunfu"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"13"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"43%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281401"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"226.55"</span> <span class="attr">cityY</span>=<span class="string">"304.5"</span> <span class="attr">cityname</span>=<span class="string">"阳江"</span> <span class="attr">centername</span>=<span class="string">"阳江"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"yangjiang"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴转多云"</span> <span class="attr">tem1</span>=<span class="string">"24"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"23"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东南风"</span> <span class="attr">windPower</span>=<span class="string">"3级"</span> <span class="attr">humidity</span>=<span class="string">"63%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281801"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"275.35"</span> <span class="attr">cityY</span>=<span class="string">"214.65"</span> <span class="attr">cityname</span>=<span class="string">"肇庆"</span> <span class="attr">centername</span>=<span class="string">"肇庆"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"zhaoqing"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"西北风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"43%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280901"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"291"</span> <span class="attr">cityY</span>=<span class="string">"285"</span> <span class="attr">cityname</span>=<span class="string">"江门"</span> <span class="attr">centername</span>=<span class="string">"江门"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"jiangmen"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"38%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281101"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"313.3"</span> <span class="attr">cityY</span>=<span class="string">"160.45"</span> <span class="attr">cityname</span>=<span class="string">"清远"</span> <span class="attr">centername</span>=<span class="string">"清远"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"qingyuan"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"南风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"44%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281301"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"308.7"</span> <span class="attr">cityY</span>=<span class="string">"225"</span> <span class="attr">cityname</span>=<span class="string">"佛山"</span> <span class="attr">centername</span>=<span class="string">"佛山"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"foshan"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴转多云"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"北风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"42%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280800"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"342.7"</span> <span class="attr">cityY</span>=<span class="string">"255"</span> <span class="attr">cityname</span>=<span class="string">"中山"</span> <span class="attr">centername</span>=<span class="string">"中山"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"zhongshan"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"西北风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"41%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281701"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"340.55"</span> <span class="attr">cityY</span>=<span class="string">"300"</span> <span class="attr">cityname</span>=<span class="string">"珠海"</span> <span class="attr">centername</span>=<span class="string">"珠海"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"zhuhai"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"23"</span> <span class="attr">tem2</span>=<span class="string">"17"</span> <span class="attr">temNow</span>=<span class="string">"23"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"48%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280701"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"352.6"</span> <span class="attr">cityY</span>=<span class="string">"80"</span> <span class="attr">cityname</span>=<span class="string">"韶关"</span> <span class="attr">centername</span>=<span class="string">"韶关"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"shaoguan"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"27"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"西风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"34%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280201"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"353"</span> <span class="attr">cityY</span>=<span class="string">"196"</span> <span class="attr">cityname</span>=<span class="string">"广州"</span> <span class="attr">centername</span>=<span class="string">"广州"</span> <span class="attr">fontColor</span>=<span class="string">"FFFF00"</span> <span class="attr">pyName</span>=<span class="string">"guangzhou"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"南风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"40%"</span> <span class="attr">time</span>=<span class="string">"15:20"</span> <span class="attr">url</span>=<span class="string">"101280101"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"377"</span> <span class="attr">cityY</span>=<span class="string">"234"</span> <span class="attr">cityname</span>=<span class="string">"东莞"</span> <span class="attr">centername</span>=<span class="string">"东莞"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"dongguan"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴转多云"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"北风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"37%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281601"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"409"</span> <span class="attr">cityY</span>=<span class="string">"257"</span> <span class="attr">cityname</span>=<span class="string">"深圳"</span> <span class="attr">centername</span>=<span class="string">"深圳"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"shenzhen"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴"</span> <span class="attr">tem1</span>=<span class="string">"24"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"24"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"南风"</span> <span class="attr">windPower</span>=<span class="string">"3级"</span> <span class="attr">humidity</span>=<span class="string">"49%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280601"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"423.85"</span> <span class="attr">cityY</span>=<span class="string">"214.65"</span> <span class="attr">cityname</span>=<span class="string">"惠州"</span> <span class="attr">centername</span>=<span class="string">"惠州"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"huizhou"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云转晴"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"13"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"南风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"38%"</span> <span class="attr">time</span>=<span class="string">"15:20"</span> <span class="attr">url</span>=<span class="string">"101280301"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"442.55"</span> <span class="attr">cityY</span>=<span class="string">"141.6"</span> <span class="attr">cityname</span>=<span class="string">"河源"</span> <span class="attr">centername</span>=<span class="string">"河源"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"heyuan"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴转多云"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"15"</span> <span class="attr">temNow</span>=<span class="string">"27"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"西南风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"37%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281201"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"492"</span> <span class="attr">cityY</span>=<span class="string">"217"</span> <span class="attr">cityname</span>=<span class="string">"汕尾"</span> <span class="attr">centername</span>=<span class="string">"汕尾"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"shanwei"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云转晴"</span> <span class="attr">tem1</span>=<span class="string">"24"</span> <span class="attr">tem2</span>=<span class="string">"14"</span> <span class="attr">temNow</span>=<span class="string">"24"</span> <span class="attr">windState</span>=<span class="string">"东南风3-4级转微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"45%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101282101"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"522.55"</span> <span class="attr">cityY</span>=<span class="string">"110.45"</span> <span class="attr">cityname</span>=<span class="string">"梅州"</span> <span class="attr">centername</span>=<span class="string">"梅州"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"meizhou"</span> <span class="attr">state1</span>=<span class="string">"1"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"多云转晴"</span> <span class="attr">tem1</span>=<span class="string">"27"</span> <span class="attr">tem2</span>=<span class="string">"12"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"1级"</span> <span class="attr">humidity</span>=<span class="string">"36%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280401"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"526.8"</span> <span class="attr">cityY</span>=<span class="string">"182"</span> <span class="attr">cityname</span>=<span class="string">"揭阳"</span> <span class="attr">centername</span>=<span class="string">"揭阳"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"jieyang"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴"</span> <span class="attr">tem1</span>=<span class="string">"26"</span> <span class="attr">tem2</span>=<span class="string">"13"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"34%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281901"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"579"</span> <span class="attr">cityY</span>=<span class="string">"137.45"</span> <span class="attr">cityname</span>=<span class="string">"潮州"</span> <span class="attr">centername</span>=<span class="string">"潮州"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"chaozhou"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"0"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴"</span> <span class="attr">tem1</span>=<span class="string">"25"</span> <span class="attr">tem2</span>=<span class="string">"12"</span> <span class="attr">temNow</span>=<span class="string">"26"</span> <span class="attr">windState</span>=<span class="string">"微风"</span> <span class="attr">windDir</span>=<span class="string">"东南风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"38%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101281501"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">city</span> <span class="attr">cityX</span>=<span class="string">"566.45"</span> <span class="attr">cityY</span>=<span class="string">"179.25"</span> <span class="attr">cityname</span>=<span class="string">"汕头"</span> <span class="attr">centername</span>=<span class="string">"汕头"</span> <span class="attr">fontColor</span>=<span class="string">"FFFFFF"</span> <span class="attr">pyName</span>=<span class="string">"shantou"</span> <span class="attr">state1</span>=<span class="string">"0"</span> <span class="attr">state2</span>=<span class="string">"1"</span> <span class="attr">stateDetailed</span>=<span class="string">"晴转多云"</span> <span class="attr">tem1</span>=<span class="string">"24"</span> <span class="attr">tem2</span>=<span class="string">"13"</span> <span class="attr">temNow</span>=<span class="string">"24"</span> <span class="attr">windState</span>=<span class="string">"东北风转东风小于3级"</span> <span class="attr">windDir</span>=<span class="string">"东风"</span> <span class="attr">windPower</span>=<span class="string">"2级"</span> <span class="attr">humidity</span>=<span class="string">"54%"</span> <span class="attr">time</span>=<span class="string">"15:30"</span> <span class="attr">url</span>=<span class="string">"101280501"</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">guangdong</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>③ 调用XMLRequest，并加入RequestQueue</strong><br>代码中我将解析XML数据的城市展示在List View中：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">XMLRequest xmlRequest = <span class="keyword">new</span> XMLRequest(</span><br><span class="line">               <span class="string">"http://flash.weather.com.cn/wmaps/xml/guangdong.xml"</span>,</span><br><span class="line">               <span class="keyword">new</span> Response.Listener&lt;XmlPullParser&gt;() &#123;</span><br><span class="line">                   <span class="meta">@Override</span></span><br><span class="line">                   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(XmlPullParser response)</span> </span>&#123;</span><br><span class="line">                       <span class="keyword">try</span> &#123;</span><br><span class="line">                           <span class="keyword">int</span> eventType = response.getEventType();</span><br><span class="line">                           <span class="keyword">while</span> (eventType != XmlPullParser.END_DOCUMENT) &#123;</span><br><span class="line">                               <span class="keyword">switch</span> (eventType) &#123;</span><br><span class="line">                                   <span class="keyword">case</span> XmlPullParser.START_TAG:</span><br><span class="line">                                       String nodeName = response.getName();</span><br><span class="line">                                       <span class="keyword">if</span> (<span class="string">"city"</span>.equals(nodeName)) &#123;</span><br><span class="line">                                           String pname = response.getAttributeValue(<span class="number">2</span>);</span><br><span class="line">                                           Log.d(<span class="string">"CITY"</span>, <span class="string">"PName is"</span> + pname);</span><br><span class="line">                                           citys.add(pname);</span><br><span class="line">                                       &#125;</span><br><span class="line">                                       <span class="keyword">break</span>;</span><br><span class="line">                               &#125;</span><br><span class="line">                               eventType = response.next();</span><br><span class="line">                           &#125;</span><br><span class="line"></span><br><span class="line">                           ArrayAdapter&lt;String&gt; arrayAdapter = <span class="keyword">new</span> ArrayAdapter&lt;String&gt;(XMLRequestActivity.<span class="keyword">this</span>, android.R.layout.simple_list_item_1, citys);</span><br><span class="line">                           lv_xml_request.setAdapter(arrayAdapter);</span><br><span class="line">                       &#125; <span class="keyword">catch</span> (XmlPullParserException e) &#123;</span><br><span class="line">                           e.printStackTrace();</span><br><span class="line">                       &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                           e.printStackTrace();</span><br><span class="line">                       &#125;</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;,</span><br><span class="line">               <span class="keyword">new</span> Response.ErrorListener() &#123;</span><br><span class="line">                   <span class="meta">@Override</span></span><br><span class="line">                   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onErrorResponse</span><span class="params">(VolleyError error)</span> </span>&#123;</span><br><span class="line">                       lv_xml_request.setVisibility(View.GONE);</span><br><span class="line">                       mTextView.setVisibility(View.VISIBLE);</span><br><span class="line">                       Log.d(<span class="string">"ERROR"</span>, <span class="string">"返回XMLRequest失败"</span>);</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line">       );</span><br><span class="line">       mQueue.add(xmlRequest);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><p><strong>人品爆发，看截图</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/5.png" alt="XMLRequest" title="">                </div>                <div class="image-caption">XMLRequest</div>            </figure></p><h2 id="自定义GsonRequest"><a href="#自定义GsonRequest" class="headerlink" title="自定义GsonRequest"></a>自定义GsonRequest</h2><p>虽然Volley提供了JsonRequest为我们解析Json，但是使用JSONObject还是太麻烦了，还有很多方法可以让JSON数据解析变得更加简单，比如说GSON，所以何不如自定义一个GsonRequest呢？思路也是大同小异！</p><p><strong>① 直接上代码啦！</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GsonRequest</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">Request</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Response.Listener&lt;T&gt; mListener;</span><br><span class="line">    <span class="keyword">private</span> Gson mGson;</span><br><span class="line">    <span class="keyword">private</span> Class&lt;T&gt; mClazz;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">GsonRequest</span><span class="params">(<span class="keyword">int</span> method, String url, Class&lt;T&gt; clazz, Response.Listener&lt;T&gt; listener, Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(method, url, errorListener);</span><br><span class="line">        mGson = <span class="keyword">new</span> Gson();</span><br><span class="line">        mClazz = clazz;</span><br><span class="line">        mListener = listener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">GsonRequest</span><span class="params">(String url, Class&lt;T&gt; clazz, Response.Listener&lt;T&gt; listener, Response.ErrorListener errorListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(Method.GET, url, clazz, listener, errorListener);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Response&lt;T&gt; <span class="title">parseNetworkResponse</span><span class="params">(NetworkResponse response)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            response.headers.put(<span class="string">"HTTP.CONTENT_TYPE"</span>, <span class="string">"utf-8"</span>);</span><br><span class="line">            String jsonString = <span class="keyword">new</span> String(response.data,<span class="string">"utf-8"</span>);</span><br><span class="line">            <span class="comment">//加上这两行可以解决乱码的问题，尤其是对于中文的json接口，由于每个网页的编码格式不同，所以获取原编码格式之后再转换为utf-8，即可。</span></span><br><span class="line">            <span class="keyword">return</span> Response.success(mGson.fromJson(jsonString, mClazz),</span><br><span class="line">                    HttpHeaderParser.parseCacheHeaders(response)</span><br><span class="line">            );</span><br><span class="line">        &#125; <span class="keyword">catch</span> (UnsupportedEncodingException e) &#123;</span><br><span class="line">            <span class="keyword">return</span> Response.error(<span class="keyword">new</span> ParseError(e));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">deliverResponse</span><span class="params">(T response)</span> </span>&#123;</span><br><span class="line">        mListener.onResponse(response);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>同样需要注意编码的问题。在parseNetworkResponse()方法中，先是将服务器响应的数据解析出来，然后通过调用Gson的fromJson方法将数据组装成对象。在deliverResponse方法中仍然是将最终的数据进行回调。  </p></blockquote><ul><li>记得为工程加入gson库</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">compile &apos;com.google.code.gson:gson:2.8.0&apos;</span><br></pre></td></tr></table></figure><p><strong>② json接口</strong><br>使用之前使用的json接口：<a href="http://www.weather.com.cn/data/sk/101010100.html" target="_blank" rel="noopener">http://www.weather.com.cn/data/sk/101010100.html</a> ，它返回的数据是一个叫做weatherinfo的jsonObject：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&quot;weatherinfo&quot;:&#123;&quot;city&quot;:&quot;北京&quot;,&quot;cityid&quot;:&quot;101010100&quot;,&quot;temp&quot;:&quot;19&quot;,&quot;WD&quot;:&quot;南风&quot;,&quot;WS&quot;:&quot;2级&quot;,&quot;SD&quot;:&quot;43%&quot;,&quot;WSE&quot;:&quot;2&quot;,&quot;time&quot;:&quot;19:45&quot;,&quot;isRadar&quot;:&quot;1&quot;,&quot;Radar&quot;:&quot;JC_RADAR_AZ9010_JB&quot;&#125;&#125;</span><br></pre></td></tr></table></figure></p><p><strong>③ 新建bean类</strong><br>由于返回的jsonObject名字叫做weatherinfo，所以我们新建一个WeatherInfo类，定义了几个最基本的属性就行了：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WeatherInfo</span></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String city;</span><br><span class="line">    <span class="keyword">private</span> String temp;</span><br><span class="line">    <span class="keyword">private</span> String time;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getCity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> city;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCity</span><span class="params">(String city)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.city = city;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getTemp</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> temp;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTemp</span><span class="params">(String temp)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.temp = temp;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">gettime</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> time;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">settime</span><span class="params">(String time)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.time = time;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>另外还需要再定义一个Weather类，来获取WeatherInfo对象：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Weather</span> </span>&#123;</span><br><span class="line">    <span class="comment">//此处的weatherinfo不可以修改为weatherInfo，因为从json获取的数据格式为</span></span><br><span class="line">    <span class="comment">// &#123;"weatherinfo":&#123;"city":"北京","cityid":"101010100","temp":"19","WD":"南风","WS":"2级","SD":"43%","WSE":"2","time":"19:45","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"&#125;&#125;</span></span><br><span class="line">    <span class="keyword">private</span> WeatherInfo weatherinfo;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> WeatherInfo <span class="title">getWeatherInfo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> weatherinfo;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWeatherInfo</span><span class="params">(WeatherInfo weatherinfo)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.weatherinfo = weatherinfo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">```  </span><br><span class="line">&gt;**此处的weatherinfo不可以修改为weatherInfo**</span><br><span class="line"></span><br><span class="line">**④调用GsonRequest**   </span><br><span class="line">我们将获取的数据显示在Text View上就行了。</span><br><span class="line">```java</span><br><span class="line">GsonRequest&lt;Weather&gt; gsonRequest=<span class="keyword">new</span> GsonRequest&lt;Weather&gt;(<span class="string">"http://www.weather.com.cn/data/sk/101010100.html"</span></span><br><span class="line">                , Weather.class,</span><br><span class="line">                <span class="keyword">new</span> Response.Listener&lt;Weather&gt;() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Weather weather)</span> </span>&#123;</span><br><span class="line">                        WeatherInfo weatherInfo = weather.getWeatherInfo();</span><br><span class="line">                        <span class="keyword">if</span> (weatherInfo != <span class="keyword">null</span>) &#123;</span><br><span class="line">                            StringBuffer str = <span class="keyword">new</span> StringBuffer();</span><br><span class="line">                            str.append(<span class="string">"城市："</span> + weatherInfo.getCity() + <span class="string">"\n"</span>);</span><br><span class="line">                            str.append(<span class="string">"温度："</span> + weatherInfo.getTemp() + <span class="string">"\n"</span>);</span><br><span class="line">                            str.append(<span class="string">"时间："</span> + weatherInfo.gettime());</span><br><span class="line">                            tv_gson_request.setText(str);</span><br><span class="line">                            Log.d(<span class="string">"SUCCESS"</span>, <span class="string">"成功返回GsonRequest  "</span> + str.toString());</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            Log.d(<span class="string">"ERROR"</span>, <span class="string">"weatherinfo对象为空"</span>);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                , <span class="keyword">new</span> Response.ErrorListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onErrorResponse</span><span class="params">(VolleyError error)</span> </span>&#123;</span><br><span class="line">                Log.d(<span class="string">"ERROR"</span>, <span class="string">"返回GsonRequest失败"</span>);</span><br><span class="line">                tv_gson_request.setText(getResources().getString(R.string.error_message));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">mQueue.add(gsonRequest);</span><br></pre></td></tr></table></figure></p><p><strong>看截图啦！</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/6.png" alt="GsonRequest" title="">                </div>                <div class="image-caption">GsonRequest</div>            </figure></p><p>这一节只是向大家介绍了如何自定义Request，其实都是大同小异的，例如你觉得Gson不能满足你，那你也可以改为FastJson或者其他的功能，但是请一定要注意编码问题！<a href="http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E5%9B%9B%EF%BC%89/" target="_blank" rel="noopener">最后一节</a>我们将一起通过阅读源码来分析Volley的工作流程！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;学习了&lt;a href=&quot;http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%BA%8C%EF%BC%89/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;一口一口吃掉Volley（二）&lt;/a&gt;之后，你应该已经学会了如何使用Volley自带的Request，但是有的时候我们需要解析的数据多种多样，例如XML又或者你想使用Google的gson，那么当Volley不能直接提供给我们这些功能的时候就需要我们进行自定义了，第一节我也向大家讲过Volley面向接口编程，使得其很容易扩展，那么这节课我们就一起来学习自定义的Request吧！&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Volley" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley/"/>
    
    
      <category term="Volley" scheme="http://www.wensibo.top/tags/Volley/"/>
    
      <category term="Android" scheme="http://www.wensibo.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Volley（二）</title>
    <link href="http://www.wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%BA%8C%EF%BC%89/"/>
    <id>http://www.wensibo.top/2017/02/17/一口一口吃掉Volley（二）/</id>
    <published>2017-02-17T05:37:25.000Z</published>
    <updated>2018-10-21T08:26:21.408Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>相信看了<a href="http://wensibo.top/2017/02/16/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%B8%80%EF%BC%89/" target="_blank" rel="noopener">第一篇教程</a>之后，你应该会对Volley有一个初步的了解了吧，那接下来就继续学习如何使用Volley进行开发吧！<br><a id="more"></a></p><h2 id="配置Gradle"><a href="#配置Gradle" class="headerlink" title="配置Gradle"></a>配置Gradle</h2><p>使用如下命令导入Volley库：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">compile &apos;com.mcxiaoke.volley:library:1.0.19&apos;</span><br></pre></td></tr></table></figure></p><ul><li>如果你还是使用Eclipse进行开发的话，可以下载<a href="http://download.csdn.net/detail/sinyu890807/7152015" target="_blank" rel="noopener">volley的jar包</a>导入工程。</li></ul><h2 id="添加联网许可"><a href="#添加联网许可" class="headerlink" title="添加联网许可"></a>添加联网许可</h2><p>在AndroidManifest.xml文件中添加联网的请求<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.INTERNET"</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="使用StringRequest"><a href="#使用StringRequest" class="headerlink" title="使用StringRequest"></a>使用StringRequest</h2><p>如果你需要通过网络访问的资源属于String字符串的资源，那么使用StringRequest就最为简单了，只需按照如下步骤就行了。</p><p><strong>① 获取一个RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RequestQueue mQueue = Volley.newRequestQueue(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure></p><p><strong>② 构造一个StringRequest对象</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">StringRequest stringRequest=<span class="keyword">new</span> StringRequest(<span class="string">"http://www.wensibo.top"</span></span><br><span class="line">        , <span class="keyword">new</span> Response.Listener&lt;String&gt;() &#123;</span><br><span class="line">           <span class="meta">@Override</span></span><br><span class="line">           <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(String response)</span> </span>&#123;</span><br><span class="line">               <span class="comment">//正常情况下的逻辑处理</span></span><br><span class="line">               String result=<span class="string">""</span>;</span><br><span class="line">               <span class="keyword">try</span> &#123;</span><br><span class="line">                    result= <span class="keyword">new</span> String(response.getBytes(<span class="string">"ISO_8859_1"</span>), <span class="string">"utf-8"</span>);</span><br><span class="line">                   <span class="comment">//由于访问string的时候可能会出现乱码情况，所以保险起见，可以将其转换为utf-8格式</span></span><br><span class="line">                   <span class="comment">//对于其他的自定义Requet都是同理的，均需要在重写parseNetworkResponse方法时设置编码格式，避免乱码的出现</span></span><br><span class="line">               &#125; <span class="keyword">catch</span> (UnsupportedEncodingException e) &#123;</span><br><span class="line">                   e.printStackTrace();</span><br><span class="line">               &#125;</span><br><span class="line">               Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">               intent.setClass(MainActivity.<span class="keyword">this</span>, StringRequestActivity.class);</span><br><span class="line">               intent.putExtra(<span class="string">"string_request"</span>, result);</span><br><span class="line">               startActivity(intent);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;, <span class="keyword">new</span> Response.ErrorListener() &#123;</span><br><span class="line">           <span class="meta">@Override</span></span><br><span class="line">           <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onErrorResponse</span><span class="params">(VolleyError error)</span> </span>&#123;</span><br><span class="line">               <span class="comment">//出错的时候做的一些处理</span></span><br><span class="line">               Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">               intent.setClass(MainActivity.<span class="keyword">this</span>, StringRequestActivity.class);</span><br><span class="line">               intent.putExtra(<span class="string">"string_request"</span>, getResources().getString(R.string.error_message));</span><br><span class="line">               startActivity(intent);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       );</span><br></pre></td></tr></table></figure></p><blockquote><p>由于不同的网页的编码格式不同，为了防止中文乱码情况的出现，代码中将其设置为utf-8即可得到解决。其他的Request如果要避免乱码的出现，也应该采取类似的方法进行处理。</p></blockquote><p><strong>③ 将StringRequest对象add进RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mQueue.add(stringRequest);</span><br></pre></td></tr></table></figure></p><p><strong>看看截图</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/1.jpg" alt="StringRequest" title="">                </div>                <div class="image-caption">StringRequest</div>            </figure></p><h2 id="使用JsonRequest"><a href="#使用JsonRequest" class="headerlink" title="使用JsonRequest"></a>使用JsonRequest</h2><p>有的时候我们不仅需要简单的String资源，还需要获取Json数据，当然Volley也帮我们提供了获取Json的接口，这个就是JsonRequest，使用JsonRequest与StringRequest类似。这里作为例子接收的是北京的天气预报的json对象，获取到的json对象直接打印出来，当然我们还可以获取里面的内容，我将会在自定义的GsonRequest中作介绍。</p><p><strong>① 获取一个RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RequestQueue mQueue = Volley.newRequestQueue(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure></p><p><strong>② 构造一个JsonRequest对象</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">JsonObjectRequest jsonObjectRequest=<span class="keyword">new</span> JsonObjectRequest(<span class="string">"http://www.weather.com.cn/data/cityinfo/101010100.html"</span></span><br><span class="line">                , <span class="keyword">new</span> Response.Listener&lt;JSONObject&gt;() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(JSONObject response)</span> </span>&#123;</span><br><span class="line">                Log.d(<span class="string">"JsonObject"</span>, response.toString());</span><br><span class="line">                Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">                intent.setClass(MainActivity.<span class="keyword">this</span>, JsonRequestActivity.class);</span><br><span class="line">                intent.putExtra(<span class="string">"json_request"</span>, response.toString());</span><br><span class="line">                startActivity(intent);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="keyword">new</span> Response.ErrorListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onErrorResponse</span><span class="params">(VolleyError error)</span> </span>&#123;</span><br><span class="line">                Log.e(<span class="string">"TAG"</span>, error.getMessage(), error);</span><br><span class="line">                Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">                intent.setClass(MainActivity.<span class="keyword">this</span>, JsonRequestActivity.class);</span><br><span class="line">                intent.putExtra(<span class="string">"json_request"</span>, getResources().getString(R.string.error_message));</span><br><span class="line">                startActivity(intent);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br></pre></td></tr></table></figure></p><p><strong>③ 将JsonRequest对象add进RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mQueue.add(jsonObjectRequest);</span><br></pre></td></tr></table></figure></p><p><strong>看看截图</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/2.png" alt="JsonRequest" title="">                </div>                <div class="image-caption">JsonRequest</div>            </figure></p><h2 id="使用ImageRequest"><a href="#使用ImageRequest" class="headerlink" title="使用ImageRequest"></a>使用ImageRequest</h2><p>获取网络上的图片资源是一个很普通的需求，虽然Volley有这个功能，但是放到现在来看都已不是什么稀奇事了，而且功能比较单一，所以这里就简单介绍一下。</p><p><strong>① 获取一个RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RequestQueue mQueue = Volley.newRequestQueue(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure></p><p><strong>② 构造一个ImageRequest对象</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">ImageRequest imageRequest=<span class="keyword">new</span> ImageRequest(<span class="string">"http://wensibo.top/img/avatar.jpg"</span></span><br><span class="line">                , <span class="keyword">new</span> Response.Listener&lt;Bitmap&gt;() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Bitmap response)</span> </span>&#123;</span><br><span class="line">                Log.d(<span class="string">"SUCCESS"</span>, <span class="string">"成功返回ImageRequest"</span>);</span><br><span class="line">                iv_image_request.setImageBitmap(response);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="number">0</span>, <span class="number">0</span>, Bitmap.Config.RGB_565, <span class="keyword">new</span> Response.ErrorListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onErrorResponse</span><span class="params">(VolleyError error)</span> </span>&#123;</span><br><span class="line">                Log.d(<span class="string">"ERROR"</span>, <span class="string">"返回ImageRequest失败"</span>);</span><br><span class="line">                iv_image_request.setImageResource(R.drawable.failed_image);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        );</span><br></pre></td></tr></table></figure></p><blockquote><p>可以看到，ImageRequest的构造函数接收六个参数，第一个参数就是图片的URL地址，这个没什么需要解释的。第二个参数是图片请求成功的回调，这里我们把返回的Bitmap参数设置到ImageView中。第三第四个参数分别用于指定允许图片最大的宽度和高度，如果指定的网络图片的宽度或高度大于这里的最大值，则会对图片进行压缩，指定成0的话就表示不管图片有多大，都不会进行压缩。第五个参数用于指定图片的颜色属性，Bitmap.Config下的几个常量都可以在这里使用，其中ARGB_8888可以展示最好的颜色属性，每个图片像素占据4个字节的大小，而RGB_565则表示每个图片像素占据2个字节大小。第六个参数是图片请求失败的回调，这里我们当请求失败时在ImageView中显示一张默认图片。</p></blockquote><p><strong>③ 将JsonRequest对象add进RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mQueue.add(jsonObjectRequest);</span><br></pre></td></tr></table></figure></p><p><strong>看看截图</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/3.jpg" alt="ImageRequest加载成功" title="">                </div>                <div class="image-caption">ImageRequest加载成功</div>            </figure><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/3.1.gif" alt="ImageRequest加载失败" title="">                </div>                <div class="image-caption">ImageRequest加载失败</div>            </figure></p><h2 id="使用ImageLoader"><a href="#使用ImageLoader" class="headerlink" title="使用ImageLoader"></a>使用ImageLoader</h2><p>Volley在请求网络图片方面除了ImageRequest之外，还有另外一个更加高效的ImageLoader。ImageLoader用于加载网络上的图片，并且它的内部也是使用ImageRequest来实现的，不过ImageLoader明显要比ImageRequest更加高效，因为它不仅可以帮我们对图片进行缓存，还可以过滤掉重复的链接，避免重复发送请求。<br>由于ImageLoader已经不是继承自Request的了，所以它的用法也和我们之前学到的内容有所不同，总结起来大致可以分为以下四步：</p><p><strong>① 获取一个RequestQueue</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RequestQueue mQueue = Volley.newRequestQueue(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure></p><p><strong>② 构造一个ImageLoader对象</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ImageLoader imageLoader = <span class="keyword">new</span> ImageLoader(mQueue, <span class="keyword">new</span> ImageCache() &#123;  </span><br><span class="line">    <span class="meta">@Override</span>  </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">putBitmap</span><span class="params">(String url, Bitmap bitmap)</span> </span>&#123;  </span><br><span class="line">    &#125;  </span><br><span class="line">  </span><br><span class="line">    <span class="meta">@Override</span>  </span><br><span class="line">    <span class="function"><span class="keyword">public</span> Bitmap <span class="title">getBitmap</span><span class="params">(String url)</span> </span>&#123;  </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;  </span><br><span class="line">    &#125;  );</span><br></pre></td></tr></table></figure></p><blockquote><p>ImageLoader的构造函数接收两个参数，第一个参数就是RequestQueue对象，第二个参数是一个ImageCache对象，这里我们先new出一个空的ImageCache的实现即可。具体的ImageCache待会讲解。</p></blockquote><p><strong>③构造一个ImageListener对象</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ImageListener listener = imageLoader.getImageListener(iv_image_loader, R.drawable.failed_image, R.drawable.failed_image);</span><br></pre></td></tr></table></figure></p><blockquote><p>getImageListener()方法接收三个参数，第一个参数指定用于显示图片的ImageView控件，第二个参数指定加载图片的过程中显示的图片，第三个参数指定加载图片失败的情况下显示的图片。</p></blockquote><p><strong>④调用ImageLoader的get()方法加载网络上的图片</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">imageLoader.get(<span class="string">"http://wensibo.top/img/avatar.jpg"</span>, listener);</span><br></pre></td></tr></table></figure></p><blockquote><p>最后，调用ImageLoader的get()方法来加载图片，get()方法接收两个参数，第一个参数就是图片的URL地址，第二个参数则是刚刚获取到的ImageListener对象。当然，如果你想对图片的大小进行限制，也可以使用get()方法的重载，指定图片允许的最大宽度和高度，如下所示：</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">imageLoader.get(<span class="string">"http://wensibo.top/img/avatar.jpg"</span>, listener,<span class="number">200</span>,<span class="number">200</span>);</span><br></pre></td></tr></table></figure><h3 id="关于ImageCache"><a href="#关于ImageCache" class="headerlink" title="关于ImageCache"></a>关于ImageCache</h3><p>刚才讲到ImageLoader的构造函数的第二个参数是一个ImageCache对象，刚才介绍的ImageLoader的优点也在于此。他能起到图片缓存的作用。接下来我们就自己写一个呗！<br>这里我们新建一个BitmapCache并实现了ImageCache接口：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BitmapCache</span> <span class="keyword">implements</span> <span class="title">ImageCache</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> LruCache&lt;String, Bitmap&gt; mCache;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BitmapCache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//将缓存图片的大小设置为8M</span></span><br><span class="line">        <span class="keyword">int</span> maxSize = <span class="number">8</span> * <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line">        mCache = <span class="keyword">new</span> LruCache&lt;String, Bitmap&gt;(maxSize)&#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">sizeOf</span><span class="params">(String key, Bitmap bitmap)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> bitmap.getRowBytes() * bitmap.getHeight();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Bitmap <span class="title">getBitmap</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mCache.get(url);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">putBitmap</span><span class="params">(String url, Bitmap bitmap)</span> </span>&#123;</span><br><span class="line">        mCache.put(url, bitmap);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>这里我们将缓存图片的大小设置为8M。接着修改创建ImageLoader实例的代码，第二个参数传入BitmapCache的实例：</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ImageLoader imageLoader = <span class="keyword">new</span> ImageLoader(mQueue, <span class="keyword">new</span> BitmapCache());</span><br></pre></td></tr></table></figure><p><strong>看看效果吧！</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/5.gif" alt="ImageLoader" title="">                </div>                <div class="image-caption">ImageLoader</div>            </figure></p><p><a href="http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%B8%89%EF%BC%89/" target="_blank" rel="noopener">下节课</a>我们将一起自定义Request来满足我们的使用！come on ！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;相信看了&lt;a href=&quot;http://wensibo.top/2017/02/16/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%B8%80%EF%BC%89/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;第一篇教程&lt;/a&gt;之后，你应该会对Volley有一个初步的了解了吧，那接下来就继续学习如何使用Volley进行开发吧！&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Volley" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley/"/>
    
    
      <category term="Volley" scheme="http://www.wensibo.top/tags/Volley/"/>
    
      <category term="Android" scheme="http://www.wensibo.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Volley（一）</title>
    <link href="http://www.wensibo.top/2017/02/16/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%B8%80%EF%BC%89/"/>
    <id>http://www.wensibo.top/2017/02/16/一口一口吃掉Volley（一）/</id>
    <published>2017-02-16T14:14:25.000Z</published>
    <updated>2018-10-21T08:26:21.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>本次编写的Volley教程现在看来其实已经跟不上时代了，但是技术总有它光辉的一面。所以想和大家一起通过源码解析的方式学习优秀开源项目的精髓。<br>本次教程将先会和大家一起了解一下Volley的前世今生，摸清他的底细之后再学会如何使用，我已经做好一个Volley的<a href="https://github.com/Wensibob/VolleySample" target="_blank" rel="noopener">Sample</a>了，欢迎你star或者fork，最后才从源码的角度上来分析Volley的工作流程。<br><a id="more"></a><br>网上关于Volley的文章很多，但是反复看了很久，值得推荐的依旧是<a href="http://blog.csdn.net/guolin_blog" target="_blank" rel="noopener">郭霖</a>的文章，写的真心不错。我也借鉴了他的很多思路，不过也做了修改，希望大家一起学习。</p><blockquote><p><a href="http://blog.csdn.net/guolin_blog/article/details/17482095" target="_blank" rel="noopener">Android Volley完全解析(一)，初识Volley的基本用法</a><br><a href="http://blog.csdn.net/guolin_blog/article/details/17482165" target="_blank" rel="noopener">Android Volley完全解析(二)，使用Volley加载网络图片</a><br><a href="http://blog.csdn.net/guolin_blog/article/details/17612763" target="_blank" rel="noopener">Android Volley完全解析(三)，定制自己的Request </a><br><a href="http://blog.csdn.net/guolin_blog/article/details/17656437" target="_blank" rel="noopener">Android Volley完全解析(四)，带你从源码的角度理解Volley</a><br><a href="https://developer.android.google.cn/training/volley/index.html" target="_blank" rel="noopener">Android官方Volley教程（英文好的同学可以自学）</a><br><a href="https://github.com/mcxiaoke/android-volley" target="_blank" rel="noopener">Volley的Github主页</a> </p></blockquote><h2 id="瞧瞧Volley是何物"><a href="#瞧瞧Volley是何物" class="headerlink" title="瞧瞧Volley是何物"></a>瞧瞧Volley是何物</h2><h3 id="Volley简介"><a href="#Volley简介" class="headerlink" title="Volley简介"></a>Volley简介</h3><p>Google在2013年的I/O大会上(是的今年的IO大会很快就会发布android8.0了)正式发布了Volley，google官方称Volley是Android平台上的网络通信库，能使网络通信更快，更简单，更健壮。<br>Volley名称的由来是：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a burst or emission of many things or a large amount at once.</span><br><span class="line">突然或大量的东西或大量的一次。</span><br></pre></td></tr></table></figure></p><p>别想歪了哦😏，所以顾名思义，Volley特别适合数据量不大但是通信频繁的场景。</p><h3 id="Volley的用武之地"><a href="#Volley的用武之地" class="headerlink" title="Volley的用武之地"></a>Volley的用武之地</h3><p>Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身，既可以像AsyncHttpClient一样非常简单地进行HTTP通信，也可以像Universal-Image-Loader一样轻松加载网络上的图片。除了简单易用之外，Volley在性能方面也进行了大幅度的调整，它的设计目标就是非常适合去进行数据量不大，但通信频繁的网络操作，而对于大数据量的网络操作，比如说下载文件等，Volley的表现就会非常糟糕。</p><p><strong>Volley提供给我们的功能</strong></p><ul><li>Json，图像等异步下载</li><li>网络请求的排序（scheduling）</li><li>网络请求的优先级处理</li><li>缓存</li><li>多级别取消请求</li><li>和 Activity 的生命周期联动（Activity 结束时同时取消所有网络请求）</li></ul><h3 id="Volley的优点"><a href="#Volley的优点" class="headerlink" title="Volley的优点"></a>Volley的优点</h3><ul><li>非常适合进行数据量不大，但通信频繁的网络操作</li><li>可直接在主线程调用服务端并处理返回结果（这一点可以很方便的更新UI）</li><li>可以取消请求，容易扩展，面向接口编程</li><li>网络请求线程NetworkDispatcher默认开启了4个和1个CacheDispatcher总共5个线程</li><li>通过使用标准的HTTP缓存机制保持磁盘和内存响应的一致</li><li>一定程度符合 Http 规范，包括返回 ResponseCode(2xx、3xx、4xx、5xx）的处理，请求头的处理，缓存机制的支持等。并支持重试及优先级定义</li><li>默认 Android2.3 及以上基于 HttpURLConnection，2.3 以下基于 HttpClient 实现</li></ul><h3 id="Volley的缺点"><a href="#Volley的缺点" class="headerlink" title="Volley的缺点"></a>Volley的缺点</h3><ul><li>使用的是httpclient、HttpURLConnection</li><li>6.0不支持httpclient了，如果想支持得添加org.apache.http.legacy.jar</li><li>对大文件下载 Volley的表现非常糟糕</li><li>只支持http请求</li><li>图片加载性能一般</li><li>最重要的一点就是现在已经过时了，已经有其他诸如okhttp等优秀开源框架可以代替</li></ul><p>最后先给大家看一下最终程序的运行效果<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-2-17volley/all.gif" alt="Volley程序截图" title="">                </div>                <div class="image-caption">Volley程序截图</div>            </figure></p><p><a href="http://wensibo.top/2017/02/17/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley%EF%BC%88%E4%BA%8C%EF%BC%89/" target="_blank" rel="noopener">下节课</a>我们讲一起编写一个<a href="https://github.com/Wensibob/VolleySample" target="_blank" rel="noopener">小程序</a>来实现Volley的一些基本功能，see u ^_+</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本次编写的Volley教程现在看来其实已经跟不上时代了，但是技术总有它光辉的一面。所以想和大家一起通过源码解析的方式学习优秀开源项目的精髓。&lt;br&gt;本次教程将先会和大家一起了解一下Volley的前世今生，摸清他的底细之后再学会如何使用，我已经做好一个Volley的&lt;a href=&quot;https://github.com/Wensibob/VolleySample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Sample&lt;/a&gt;了，欢迎你star或者fork，最后才从源码的角度上来分析Volley的工作流程。&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Volley" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Volley/"/>
    
    
      <category term="Volley" scheme="http://www.wensibo.top/tags/Volley/"/>
    
      <category term="Android" scheme="http://www.wensibo.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（六）【已修复问题】</title>
    <link href="http://www.wensibo.top/2017/01/13/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo%EF%BC%88%E5%85%AD%EF%BC%89%E3%80%90%E5%B7%B2%E4%BF%AE%E5%A4%8D%E9%97%AE%E9%A2%98%E3%80%91/"/>
    <id>http://www.wensibo.top/2017/01/13/一口一口吃掉Hexo（六）【已修复问题】/</id>
    <published>2017-01-13T06:52:04.000Z</published>
    <updated>2018-10-21T08:26:21.248Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>不知不觉已经更新到了最后一节了，很开心你能看到这一节，相信你也已经在你的虚拟主机上成功部署了你的网站，但是可能总会遇到一些问题，也想让你的网站变得更好，这一节，我将与你分享一些解决错误的方法以及如何提高你网站的访问速度。一起来吧！<br>本篇文章已经得到更新，我已经将Hexo配置404页面的问题以及部署到Nginx的问题得到了解决，你可以点击<a href="http://wensibo.top/2017/01/12/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo(%E5%9B%9B" target="_blank" rel="noopener">这里</a>/)查看详细的更新<br><a id="more"></a></p><h2 id="请详细观看这份文档"><a href="#请详细观看这份文档" class="headerlink" title="请详细观看这份文档"></a>请详细观看这份文档</h2><p>如果你是使用<a href="http://www.wensibo.top">Indigo</a>主题的，，那么最重要的就是这份<a href="https://github.com/yscoder/hexo-theme-indigo/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98" target="_blank" rel="noopener">文档</a>了，里面列出了许多你可能会遇到的问题，如果这里没有列出，或者这里的回答满足不了你，请给作者发Issue，或者你也可以<a href="http://www.wensibo.top">站内留言</a></p><hr><h2 id="提高网站的访问速度"><a href="#提高网站的访问速度" class="headerlink" title="提高网站的访问速度"></a>提高网站的访问速度</h2><p>最开始的时候网站的访问速度出奇的慢，在控制台观察知道是头像加载的时候拖慢了速度，要知道我的头像只有300K，无奈只能压缩图片大小了，推荐这个<a href="http://www.tuhaokuai.com/image" target="_blank" rel="noopener">在线压缩图片</a>的网站，把头像压缩成了20K，这下加载就不会龟速了。</p><hr><h2 id="推广你的网站"><a href="#推广你的网站" class="headerlink" title="推广你的网站"></a>推广你的网站</h2><p>网站推广其实质就是SEO(搜索引擎优化)，提高你网站在网络的可见度，那么可以通过很多方式去增加网站的访问量。</p><ul><li>向搜索引擎提交你的网站地址。这是至关重要的，也是十分有效的。</li><li>如果你有访问量比较客观的网络工具，例如<a href="http://www.cnblogs.com/ghylzwsb/" target="_blank" rel="noopener">我的博客园博客</a>，可以在上面增加你网站的外链，吸引用户来访问你的网站。</li><li>在社交工具上分享你的网站内容，并附上你的链接。</li><li>优化好你的网站，这个是核心，搜索引擎只有觉得你的网站是面向用户的才会给你加分。<br>如果你能做到以上列出的4点，相信你的网站访问量会很可观，当然我也在持续努力中。</li></ul><hr><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>本次系列教程到这里就结束啦，如果你对Hexo建站有什么问题或者对我的教程有任何的建议，请你联系我，让我们一起分享吧！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;不知不觉已经更新到了最后一节了，很开心你能看到这一节，相信你也已经在你的虚拟主机上成功部署了你的网站，但是可能总会遇到一些问题，也想让你的网站变得更好，这一节，我将与你分享一些解决错误的方法以及如何提高你网站的访问速度。一起来吧！&lt;br&gt;本篇文章已经得到更新，我已经将Hexo配置404页面的问题以及部署到Nginx的问题得到了解决，你可以点击&lt;a href=&quot;http://wensibo.top/2017/01/12/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo(%E5%9B%9B&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;这里&lt;/a&gt;/)查看详细的更新&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（五）【有更新】</title>
    <link href="http://www.wensibo.top/2017/01/13/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo%EF%BC%88%E4%BA%94%EF%BC%89%E3%80%90%E6%9C%89%E6%9B%B4%E6%96%B0%E3%80%91/"/>
    <id>http://www.wensibo.top/2017/01/13/一口一口吃掉Hexo（五）【有更新】/</id>
    <published>2017-01-13T03:56:15.000Z</published>
    <updated>2018-10-21T08:26:21.348Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>通过前四节的内容，相信你已经能够在你的虚拟主机上成功部署网站，并且能够通过你自己的域名访问你的网站了，接下来要做的就是日常管理你的文章了。包括如何新建一篇文章，删除文章以及修改文章当然还有同步到Github啦，一起来吧！<br><a id="more"></a></p><h2 id="在虚拟主机中管理文章"><a href="#在虚拟主机中管理文章" class="headerlink" title="在虚拟主机中管理文章"></a>在虚拟主机中管理文章</h2><h3 id="新建一篇文章"><a href="#新建一篇文章" class="headerlink" title="新建一篇文章"></a>新建一篇文章</h3><p>使用如下命令你可以新建一篇文章<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo new &lt;title&gt;</span><br><span class="line">例如</span><br><span class="line">hexo new  第一篇文章</span><br></pre></td></tr></table></figure></p><p>当然你也可以直接在<code>blog\source\_posts</code>目录下直接新建一个yourtitle.md文件，这样就算是新建了一篇文章，但是这样创建的文章需要在最上方添加如下说明：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">title: hello world</span><br><span class="line">date: 2017-01-12 20:37:15</span><br><span class="line">tags:</span><br><span class="line">- hello world</span><br><span class="line">- hello</span><br><span class="line">categories: [first]</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p><ul><li>请注意hexo的格式比较严格，必须要在属性后面的冒号之后紧跟一个空格，后面跟内容。</li><li>如果是标签的话，在 <code>-</code> 后紧跟一个空格，再填写标签内容。</li><li>如果有多个标签，可以换行以此按照原来的格式继续填写。</li><li>分类的话，我尝试了多次，只有再添加一个分类的时候才能正常显示，如果你想在一片文章中添加多个分类的话，好像是会有如下图所示的问题的，当然我觉得每篇文章属于一个分类就够了吧！</li></ul><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-13T08-88-11.323Z.PNG" alt="多categories异常" title="">                </div>                <div class="image-caption">多categories异常</div>            </figure><ul><li>如果想要显示文章的摘要，那就这样写，需要说明的是，摘要也会出现在文章的详细页面中，你可以看看我的<a href="http://www.wensibo.top">文章</a> 。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">这里写摘要，但是在文章详细页面同样也是会出现的！</span><br><span class="line">&lt;!--more--&gt;</span><br><span class="line">这里写正文</span><br></pre></td></tr></table></figure></li></ul><h3 id="删除一篇文章"><a href="#删除一篇文章" class="headerlink" title="删除一篇文章"></a>删除一篇文章</h3><p>hexo似乎没有专门的命令来删除一篇文章吧，删除的手法也很简单，就是在<code>blog\source\_posts</code>目录下直接删除指定文章，刷新一下你的网站就可以看到变化了。</p><h3 id="修改一篇文章"><a href="#修改一篇文章" class="headerlink" title="修改一篇文章"></a>修改一篇文章</h3><p>文章的修改应该是很频繁的吧，其实就是打开你想修改的文件，直接在里面修改保存，但是这样似乎见不到效果，很多时候你刷新一下页面只能见到如下情况：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-13T09-88-11.323Z.PNG" alt="NoTitle" title="">                </div>                <div class="image-caption">NoTitle</div>            </figure><br>如果真的遇到了，最粗暴的方法就是在git上使用上按住键盘Ctrl+C关闭服务器，之后再使用 <code>hexo s</code> 开启服务器，就可以看到变化了。</p><h3 id="开始将文章发布到你的网站"><a href="#开始将文章发布到你的网站" class="headerlink" title="开始将文章发布到你的网站"></a>开始将文章发布到你的网站</h3><blockquote><p>每一次修改完文章，包括你删除文章，都需要使用如下命令将生成新的静态页面，这些页面将会存放在blog/public文件夹下，所以每一次都需要将这个文件夹直接拷贝到nginx/html文件夹下，这样才能保证每一次你发布的文章能够及时的更新。</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo g</span><br></pre></td></tr></table></figure><hr><h2 id="同步你的文章内容到Github"><a href="#同步你的文章内容到Github" class="headerlink" title="同步你的文章内容到Github"></a>同步你的文章内容到Github</h2><p>很多时候，为了以防服务器遭受不测，避免你话费许久写好的文章不翼而飞，就备份到Github中的 <strong>yourname.github.io</strong> 项目吧！<br>首先先试用下面的命令生成本地静态文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo g</span><br><span class="line">或者</span><br><span class="line">hexo generate</span><br></pre></td></tr></table></figure></p><p>接着使用deploy命令部署同步到github<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo d</span><br><span class="line">或者</span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure></p><p>我不会告诉你使用下面的命令可以一步到位<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo g -d</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;通过前四节的内容，相信你已经能够在你的虚拟主机上成功部署网站，并且能够通过你自己的域名访问你的网站了，接下来要做的就是日常管理你的文章了。包括如何新建一篇文章，删除文章以及修改文章当然还有同步到Github啦，一起来吧！&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（四）【更新了错误】</title>
    <link href="http://www.wensibo.top/2017/01/12/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo%EF%BC%88%E5%9B%9B%EF%BC%89%E3%80%90%E6%9B%B4%E6%96%B0%E4%BA%86%E9%94%99%E8%AF%AF%E3%80%91/"/>
    <id>http://www.wensibo.top/2017/01/12/一口一口吃掉Hexo（四）【更新了错误】/</id>
    <published>2017-01-12T13:56:15.000Z</published>
    <updated>2018-10-21T08:26:21.316Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>人总是不会满足于现状，接下来我们就可以让我们的朋友们通过独立域名访问我们的网站了，但是这肯定是要花点钱的，所以这篇文章难免会出现一些推销信息，希望这会对你有用，话不多说动手吧！<br><a id="more"></a></p><h2 id="购买一个域名"><a href="#购买一个域名" class="headerlink" title="购买一个域名"></a>购买一个域名</h2><p>我的域名是在<a href="https://www.aliyun.com/" target="_blank" rel="noopener">阿里云</a>购买的，因为是.top域名，所以比较便宜，第一年才3块钱，如果是土豪，就买其他的吧，但是如果你现在是在校大学生的话，腾讯云的<a href="https://www.qcloud.com/act/campus" target="_blank" rel="noopener">学生优惠</a>活动可以一元钱购买域名和空间，不是为最佳的选择。<br><strong>如果你选择了腾讯云的学生优惠活动，那么请你先注册好账号，并且通过实名认证以及学生认证，这样才可以开始学生优惠活动的抢购活动，活动开抢时间为每天的中午12点，调好闹钟摆好姿势啦，不过我觉得挺好抢的。祝好运😉<br>如果你已经买好了域名，并且不打算买空间，只想用Github上的存储空间，那就来吧！</strong></p><h3 id="域名解析"><a href="#域名解析" class="headerlink" title="域名解析"></a>域名解析</h3><p>来到你域名的管理系统，点击域名解析，添加两条解析，如图所示：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-12T08-49-11.323Z.PNG" alt="域名解析" title="">                </div>                <div class="image-caption">域名解析</div>            </figure><br>两条记录类型都是CNAME，主机记录分别为www以及@，线路最好选择海外，毕竟Github对吧😙，记录值则是写你的yourname.github.io。<br>同样等待几分钟之后就可以进行访问了，你会发现如果你在访问<a href="https://yourname.github.io" target="_blank" rel="noopener">https://yourname.github.io</a> ，它会自动转到你的域名，当然你也可以直接使用你的域名进行访问。</p><hr><h2 id="购买空间"><a href="#购买空间" class="headerlink" title="购买空间"></a>购买空间</h2><p>域名我是在阿里云很早就买了，但是空间是在腾讯云买的，我买的虚拟主机是Windows Server2012 版本，当然主机的类型对网站的搭建并没有多大影响，基本的操作大同小异。</p><h3 id="域名解析-1"><a href="#域名解析-1" class="headerlink" title="域名解析"></a>域名解析</h3><p>如果你还买了空间，那就同样来到你域名的管理系统，点击域名解析，应当按照如下所示的进行设置：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-12T08-88-11.323Z.PNG" alt="域名解析" title="">                </div>                <div class="image-caption">域名解析</div>            </figure></p><hr><h2 id="在虚拟主机中搭建你的网站"><a href="#在虚拟主机中搭建你的网站" class="headerlink" title="在虚拟主机中搭建你的网站"></a>在虚拟主机中搭建你的网站</h2><p>这一步其实就是将你在本地的工作重复进行一遍，请见<a href="http://www.wensibo.top/2017/01/12/一口一口吃掉Hexo(二">第二节</a>/)，按照这一节可以在虚拟主机中访问localhost:4000进入你的网站。</p><h3 id="使用nginx反向代理"><a href="#使用nginx反向代理" class="headerlink" title="使用nginx反向代理"></a>使用nginx反向代理</h3><p>其实经过上一步，你的朋友们可以通过yourwebsitename:4000访问你的网站了，但是这样也太不帅了吧，所以我们可以使用nginx监听80端口，将所有的请求返回80端口，这样你的朋友只要访问你的域名就可以直接进入网站主页了，而不用加上4000端口。</p><ul><li>下载<a href="http://nginx.org/en/download.html" target="_blank" rel="noopener">Nginx</a>并解压至c:\nginx。</li><li>启动Nginx。进入c:\nginx，双击nginx.exe，或者打开cmd，进入Nginx目录，执行命令start nginx。默认的端口号为80。<blockquote><p>直接访问浏览器localhost,正常情况下，就能看到Nginx的欢迎界面了。如果不对，90%的可能是因为80端口占用问题，打开配置Nginx配置文件，修改一下默认端口就行了。</p></blockquote></li><li>修改端口号。修改c:\nginx\conf\nginx.conf，将其中的server节点修改如下，记得要把最后一行错误页也修改了，这样当你访问你的网站的时候，出现路径不存在的情况时，会自动跳转到该页面：</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    listen       80;</span><br><span class="line">    server_name  localhost;</span><br><span class="line"></span><br><span class="line">    #charset koi8-r;</span><br><span class="line"></span><br><span class="line">    #access_log  logs/host.access.log  main;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        root   html/public;</span><br><span class="line">        index  index.html;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    error_page  404              /404.html;</span><br></pre></td></tr></table></figure><ul><li><p>通过Hexo g命令生成的静态站点，默认就是Hexo站点目录中的public文件夹。将生成好的静态站点（也就是public/目录），拷贝至Nginx目录下的html文件夹中。然后修改Nginx配置文件。记得将public文件夹全部拷贝哦！</p></li><li><p>重启nginx。在任务管理器关闭nginx.exe再重新开启或者在命令行中使用如下命令重启nginx。</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nginx -s reload</span><br></pre></td></tr></table></figure><ul><li>重新访问localhost，就可以看到Hexo静态站点了。这里要注意浏览器缓存的问题。</li></ul><hr><h2 id="开心吗？"><a href="#开心吗？" class="headerlink" title="开心吗？"></a>开心吗？</h2><p>如果幸运的话，到此为止，你就可以使用yourwebsitename访问你的网站了，如果有任何问题，可以在<a href="http://www.wensibo.top">站内</a>留言或者自行google。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;人总是不会满足于现状，接下来我们就可以让我们的朋友们通过独立域名访问我们的网站了，但是这肯定是要花点钱的，所以这篇文章难免会出现一些推销信息，希望这会对你有用，话不多说动手吧！&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（三）</title>
    <link href="http://www.wensibo.top/2017/01/12/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo(%E4%B8%89)/"/>
    <id>http://www.wensibo.top/2017/01/12/一口一口吃掉Hexo(三)/</id>
    <published>2017-01-12T12:37:15.000Z</published>
    <updated>2018-10-21T08:26:21.280Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>相信通过前一节的学习，你已经在你的本地部署好了你的网站，那么接下来就让你的朋友们通过网络访问你的网站吧！通过这一节你将免费拥有一个“域名”（其实是一个二级域名，但是真的是免费的哦），同时你也会拥有一个免费的存储空间，用于存放你网站的文件资料。那就跟着我来吧！！！<br><a id="more"></a></p><h2 id="新建一个仓库"><a href="#新建一个仓库" class="headerlink" title="新建一个仓库"></a>新建一个仓库</h2><p><strong>如果你还没有自己的Github账户，那就那就返回上一节创建Github账户，并且配置好本地的Git，使之与Github账户建立连接。</strong></p><ul><li>新建一个仓库，名字为<strong>yourname.github.io</strong>（例如我的github账户名称为Wensibob），那么名字就是<strong>Wensibob.github.io</strong>。</li></ul><hr><h2 id="修改-config-yml"><a href="#修改-config-yml" class="headerlink" title="修改_config.yml"></a>修改_config.yml</h2><p>记得上一节我讲过_config.yml文件，我们会经常跟他打交道，对的就是他啦！记得是根目录下的_config.yml文件哦！</p><ul><li><p>按照如下的代码修改该文件中的deploy部分。同样将<strong>yourname</strong>改为你的账户名字</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">deploy:</span><br><span class="line">  type: git</span><br><span class="line">  repo: https://github.com/yourname/yourname.github.io.git</span><br><span class="line">  branch: master</span><br></pre></td></tr></table></figure></li><li><p>记住下面的额写法是错的哦！别进坑：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">deploy:</span><br><span class="line">  type: github</span><br><span class="line">  repo: https://github.com/yourname/yourname.github.io.git</span><br><span class="line">  branch: master</span><br></pre></td></tr></table></figure></li><li><p>安装Hexo支持Git的插件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure></li><li><p>生成静态文件，基本上你每次修改你的文章，都需要用到它</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo g</span><br><span class="line">或者</span><br><span class="line">hexo generate</span><br></pre></td></tr></table></figure></li><li><p>部署你的网站</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo d</span><br><span class="line">或者</span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure></li></ul><p>知道Git提示你<code>Deploy Done</code>，就说明你部署成功了，接下来刷新一下你的Github的这个项目就会发现已经push进来了。</p><hr><h2 id="等待片刻吧！"><a href="#等待片刻吧！" class="headerlink" title="等待片刻吧！"></a>等待片刻吧！</h2><p>不知道为什么，如果你现在就访问<a href="https://yourname.github.io" target="_blank" rel="noopener">https://yourname.github.io</a> （yourname换成你的账户名字），你会发现并没有用，但是稍待片刻，或许是5分钟吧，你就可以正常访问了，但是记住如果你使用http访问的话，你可能会发现并访问不了，这个时候可以使用https进行访问。</p><p>下一节你将可以通过你自己的域名访问你的网站。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;相信通过前一节的学习，你已经在你的本地部署好了你的网站，那么接下来就让你的朋友们通过网络访问你的网站吧！通过这一节你将免费拥有一个“域名”（其实是一个二级域名，但是真的是免费的哦），同时你也会拥有一个免费的存储空间，用于存放你网站的文件资料。那就跟着我来吧！！！&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（二）【新增配置404】</title>
    <link href="http://www.wensibo.top/2017/01/12/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo%EF%BC%88%E4%BA%8C%EF%BC%89%E3%80%90%E6%96%B0%E5%A2%9E%E9%85%8D%E7%BD%AE404%E3%80%91/"/>
    <id>http://www.wensibo.top/2017/01/12/一口一口吃掉Hexo（二）【新增配置404】/</id>
    <published>2017-01-12T09:34:25.000Z</published>
    <updated>2018-10-21T08:26:21.216Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>本次系列教程的第二篇文章我会介绍如何在本地安装Hexo，请注意我使用的Windows系统，如果你是Mac或者Ubuntu，请参考<a href="https://hexo.io/zh-cn/docs/" target="_blank" rel="noopener">官方文档</a>，理论上来说会比windows简单。<br><a id="more"></a></p><h2 id="本地安装Hexo"><a href="#本地安装Hexo" class="headerlink" title="本地安装Hexo"></a>本地安装Hexo</h2><h3 id="安装Git"><a href="#安装Git" class="headerlink" title="安装Git"></a>安装Git</h3><ul><li>下载并安装<a href="https://git-scm.com/download/win" target="_blank" rel="noopener">Git</a>。</li><li>如果你已经在本地使用过Git，并且绑定了你的Github账号，那么请看<a href="#安装Node.js">下一步</a>吧!如果你是首次使用Git，那么你应当帮顶一下你的Github账号，在<a href="https://github.com/" target="_blank" rel="noopener">这里</a>注册你的Github账号。</li><li><p>使用下面的命令创建SSH Key。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ ssh-keygen -t rsa -C &quot;youremail@example.com&quot;</span><br></pre></td></tr></table></figure></li><li><p>如果一切顺利的话，可以在用户主目录里找到.ssh目录，里面有<strong>id_rsa</strong>和<strong>id_rsa.pub8</strong>两个文件，这两个就是SSH Key的秘钥对，<strong>id_rsa</strong>是私钥，不能泄露出去，<strong>id_rsa.pub</strong>是公钥，可以放心地告诉任何人。</p></li><li>登陆GitHub，打开“Account settings”，“SSH Keys”页面，然后，点“Add SSH Key”，填上任意Title，在Key文本框里粘贴id_rsa.pub文件的内容，点“Add Key”，你就应该看到已经添加的Key。</li></ul><h3 id="安装Node-js"><a href="#安装Node-js" class="headerlink" title="安装Node.js"></a>安装Node.js</h3><p>到<a href="http://nodejs.org/" target="_blank" rel="noopener">这里</a>下载并安装程序。</p><h3 id="安装Hexo"><a href="#安装Hexo" class="headerlink" title="安装Hexo"></a>安装Hexo</h3><p>所有的环境搭建好之后就可以打开git bash，并使用下面的命令进行安装。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install -g hexo-cli</span><br></pre></td></tr></table></figure></p><h3 id="搭建网站"><a href="#搭建网站" class="headerlink" title="搭建网站"></a>搭建网站</h3><p>安装 Hexo 完成后，执行下列命令，Hexo 将会在指定文件夹中新建所需要的文件。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ hexo init &lt;folder&gt;</span><br><span class="line">$ cd &lt;folder&gt;</span><br><span class="line">$ npm install</span><br></pre></td></tr></table></figure></p><p>这个过程需要一定的时间，请耐心等待。<br>建成之后的目录结构大致如下，可能会有所差异，并无大碍。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">├── _config.yml</span><br><span class="line">├── package.json</span><br><span class="line">├── scaffolds</span><br><span class="line">├── source</span><br><span class="line">|   ├── _drafts</span><br><span class="line">|   └── _posts</span><br><span class="line">└── themes</span><br></pre></td></tr></table></figure></p><ul><li><strong>_config.yml</strong> 是网站的配置信息，大部分时候你会使用到他。</li><li><strong>source</strong> 目录下面存放的是你编写的博客文章，在 <strong>_posts</strong> 目录下为你已经发表的文章，在 <strong>_drafts</strong> 目录下为你存放的草稿文章，当然你可能没有这个文件夹，因为一开始你还没有新建草稿文章。</li><li><strong>themes</strong> 目录为你的博客网站使用的主题，程序默认的主题landscape，他长这样：</li></ul><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-12T08-13-12.446Z.png" alt="landscape" title="">                </div>                <div class="image-caption">landscape</div>            </figure><hr><h2 id="本地调试Hexo"><a href="#本地调试Hexo" class="headerlink" title="本地调试Hexo"></a>本地调试Hexo</h2><p>###启动你的网站</p><ul><li><p>使用如下命令进入你的博客网站目录，例如我的目录为/e/blog。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /e/blog</span><br></pre></td></tr></table></figure></li><li><p>执行如下命令启动Hexo服务。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo s</span><br><span class="line">或者</span><br><span class="line">hexo server</span><br></pre></td></tr></table></figure></li><li><p>如果幸运的话，执行完上面的命令之后你将可以通过浏览器访问localhost:4000看到博客网站的主页，他大概长这样，你已经看过了对吧。</p></li></ul><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-12T08-13-12.446Z.png" alt="landscape" title="">                </div>                <div class="image-caption">landscape</div>            </figure><ul><li>如果你出现的问题,请联系我或者自行google。</li></ul><hr><h2 id="本地更换主题"><a href="#本地更换主题" class="headerlink" title="本地更换主题"></a>本地更换主题</h2><p>如果你不喜欢默认主题的话，那就在<a href="https://hexo.io/themes/" target="_blank" rel="noopener">这里</a>找到你喜欢的吧！相信总有一款适合你。<br><br><strong>我这个系列的教程将会使用<a href="http://www.wensibo.top">Indigo</a>这个主题，基本来说设置大同小异，他长这样：</strong><br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://www.wensibo.top/file/img/2017-01-12T08-29-11.323Z.png" alt="Indigo" title="">                </div>                <div class="image-caption">Indigo</div>            </figure></p><h3 id="安装主题"><a href="#安装主题" class="headerlink" title="安装主题"></a>安装主题</h3><p>确认你的Hexo版本在3.0以上，以及Node版本为6.x以上，进入Hexo根目录（例如我的为/e/blog），执行以下命令。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone git@github.com:yscoder/hexo-theme-indigo.git themes/indigo</span><br></pre></td></tr></table></figure></p><h3 id="依赖安装"><a href="#依赖安装" class="headerlink" title="依赖安装"></a>依赖安装</h3><p>在Hexo的根目录下安装下面的依赖，如果你已经安装过了，那就不用再次安装。</p><h4 id="Less"><a href="#Less" class="headerlink" title="Less"></a>Less</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-renderer-less --save</span><br></pre></td></tr></table></figure><h4 id="Feed"><a href="#Feed" class="headerlink" title="Feed"></a>Feed</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-generator-feed --save</span><br></pre></td></tr></table></figure><h4 id="Json-content"><a href="#Json-content" class="headerlink" title="Json-content"></a>Json-content</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-generator-json-content --save</span><br></pre></td></tr></table></figure><h4 id="QRCode"><a href="#QRCode" class="headerlink" title="QRCode"></a>QRCode</h4><p>用于生成分享二维码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-helper-qrcode --save</span><br></pre></td></tr></table></figure></p><h3 id="开启标签页"><a href="#开启标签页" class="headerlink" title="开启标签页"></a>开启标签页</h3><p><strong>当初就是在这里没有开启，所以最后点击“标签“的时候是会出现异常的</strong><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page tags</span><br></pre></td></tr></table></figure></p><p>修改blog/source/tags/index.md</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">layout: tags</span><br><span class="line">comments: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure><h3 id="开启分类页"><a href="#开启分类页" class="headerlink" title="开启分类页"></a>开启分类页</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page categories</span><br></pre></td></tr></table></figure><p>修改blog/source/categories/index.md<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">layout: categories</span><br><span class="line">comments: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p><h3 id="大坑之404页面配置"><a href="#大坑之404页面配置" class="headerlink" title="大坑之404页面配置"></a>大坑之404页面配置</h3><p>新建一个404.html文件，具体的样式你可以自己写，网上应该也挺多的，记得将它放在<strong>blog/public</strong>下，就放这里就行了，网上很多人说放在source下，亲试不可用！</p><h3 id="开心地浏览吧！"><a href="#开心地浏览吧！" class="headerlink" title="开心地浏览吧！"></a>开心地浏览吧！</h3><p>同样是打开浏览器，访问localhost:4000，就可以看到亲切的Indigo主题了。<br><strong>如果你发现遇到了问题，请你来到<a href="https://github.com/yscoder/hexo-theme-indigo/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98" target="_blank" rel="noopener">这里</a>查看解决方法，如果这里不能满足你，请像作者发Issues，或者站内留言or给我发邮件<a href="mailto:Wensibob@gmail.com" target="_blank" rel="noopener">Wensibob@gmail.com</a></strong></p><p>下回见!!!</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本次系列教程的第二篇文章我会介绍如何在本地安装Hexo，请注意我使用的Windows系统，如果你是Mac或者Ubuntu，请参考&lt;a href=&quot;https://hexo.io/zh-cn/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官方文档&lt;/a&gt;，理论上来说会比windows简单。&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>一口一口吃掉Hexo（一）</title>
    <link href="http://www.wensibo.top/2017/01/11/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo(%E4%B8%80)/"/>
    <id>http://www.wensibo.top/2017/01/11/一口一口吃掉Hexo(一)/</id>
    <published>2017-01-11T12:43:25.000Z</published>
    <updated>2018-10-21T08:26:21.376Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>　　这里是我的个人博客网站，<a href="http://www.wensibo.top" title="blank">点击这里</a>你可以到我的首页瞧瞧。我之前使用的是第三方的博客平台——博客园，点击这里可以看到我的<a href="http://www.cnblogs.com/ghylzwsb/" title="blank" target="_blank" rel="noopener">博客园首页</a>。<br>　　在看到Hexo之后，我深深的为之着迷，尤其是看到了Material Design主题的Hexo，我多想我自己的博客网站也长这样，于是就开始动手啦，前后花费了4天啊，感觉要废了😭 这也是我写这个系列教程的原因，希望后来者能够少填坑，但是我现在目前的一些配置还不太完善，还需要自己去摸索，也希望看到这个系列文章的你尽管吐槽☺<br>　　废话的最后我要感谢我目前使用的主题Indigo的作者<a href="https://imys.net/" title="blank" target="_blank" rel="noopener">王昱森</a>，感谢他解决我的一些提问，也感谢他为了维护这个主题所做的工作；另外我的许多配置都参考了该作者的<a href="https://github.com/yscoder/hexo-theme-indigo/wiki" title="blank" target="_blank" rel="noopener">github教程</a>。<br><a id="more"></a></p><h2 id="来看看Hexo是什么吧"><a href="#来看看Hexo是什么吧" class="headerlink" title="来看看Hexo是什么吧"></a>来看看Hexo是什么吧</h2><h3 id="关于Hexo"><a href="#关于Hexo" class="headerlink" title="关于Hexo"></a>关于Hexo</h3><p>　　以下是官方对Hexo的定义:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Hexo 是一个快速、简洁且高效的博客框架。</span><br><span class="line">Hexo 使用 Markdown（或其他渲染引擎）解析文章，</span><br><span class="line">在几秒内，即可利用靓丽的主题生成静态网页。</span><br></pre></td></tr></table></figure></p><p>　　其实对于程序员来说，只要是自己喜欢的，就算不是简单的也会自己去捣鼓的，要有一个不费折腾的心。如果你对Hexo很感兴趣那我会推荐他的<a href="https://hexo.io/zh-cn/" title="blank" target="_blank" rel="noopener">官网给你</a>，在这里你可以你可以仔细阅读他的文档，当然我觉得他的文档也不够详尽，但是如果你遇到问题了，也可以在我的网站中留言或者发email给我，我会尽我所能给你帮助。</p><h3 id="关于Indigo"><a href="#关于Indigo" class="headerlink" title="关于Indigo"></a>关于Indigo</h3><p>　　Indigo使用了类似Android的MD设计模式，几乎在Google自家的网页设计中，这种设计语言运用自如，但是运用在博客网站中我却是第一次见到的，所以才会有想要试试的心态。</p><hr><h2 id="通过本次系列教程你会学会什么"><a href="#通过本次系列教程你会学会什么" class="headerlink" title="通过本次系列教程你会学会什么"></a>通过本次系列教程你会学会什么</h2><p>　　通过本系列的教程，你将会建立一个自己的博客网站，你可以拥有自己的域名，可以自己维护自己的网站，这会是一个很有诱惑的目标吧！但是为了避免你到处跳坑，所以请你务必按照我的教程一步一步配置。需要说明的是本次教程我使用的操作系统为Win10，理论上来说其他系统会比windows要更加简便，但是上了微软的船再也下不来了😐。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;　　这里是我的个人博客网站，&lt;a href=&quot;http://www.wensibo.top&quot; title=&quot;blank&quot;&gt;点击这里&lt;/a&gt;你可以到我的首页瞧瞧。我之前使用的是第三方的博客平台——博客园，点击这里可以看到我的&lt;a href=&quot;http://www.cnblogs.com/ghylzwsb/&quot; title=&quot;blank&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;博客园首页&lt;/a&gt;。&lt;br&gt;　　在看到Hexo之后，我深深的为之着迷，尤其是看到了Material Design主题的Hexo，我多想我自己的博客网站也长这样，于是就开始动手啦，前后花费了4天啊，感觉要废了😭 这也是我写这个系列教程的原因，希望后来者能够少填坑，但是我现在目前的一些配置还不太完善，还需要自己去摸索，也希望看到这个系列文章的你尽管吐槽☺&lt;br&gt;　　废话的最后我要感谢我目前使用的主题Indigo的作者&lt;a href=&quot;https://imys.net/&quot; title=&quot;blank&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;王昱森&lt;/a&gt;，感谢他解决我的一些提问，也感谢他为了维护这个主题所做的工作；另外我的许多配置都参考了该作者的&lt;a href=&quot;https://github.com/yscoder/hexo-theme-indigo/wiki&quot; title=&quot;blank&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github教程&lt;/a&gt;。&lt;br&gt;
    
    </summary>
    
      <category term="一口一口吃掉Hexo" scheme="http://www.wensibo.top/categories/%E4%B8%80%E5%8F%A3%E4%B8%80%E5%8F%A3%E5%90%83%E6%8E%89Hexo/"/>
    
    
      <category term="使用Hexo建立个人博客网站" scheme="http://www.wensibo.top/tags/%E4%BD%BF%E7%94%A8Hexo%E5%BB%BA%E7%AB%8B%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>setSupportActionBar(toolbar)导致程序崩溃闪退</title>
    <link href="http://www.wensibo.top/2016/09/06/setSupportActionBar(toolbar)%E5%AF%BC%E8%87%B4%E7%A8%8B%E5%BA%8F%E5%B4%A9%E6%BA%83%E9%97%AA%E9%80%80/"/>
    <id>http://www.wensibo.top/2016/09/06/setSupportActionBar(toolbar)导致程序崩溃闪退/</id>
    <published>2016-09-06T06:24:45.000Z</published>
    <updated>2018-10-21T08:26:20.916Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>最近在做一个项目，使用了第三方的开源项目，主要是想实现android5.0之后推出的MaterialDesign的风格，但是代码已经写好了，发现一运行就闪退，所以就开始debug，发现问题出现在<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);</span><br><span class="line">setSupportActionBar(toolbar);</span><br></pre></td></tr></table></figure></p><a id="more"></a><p>很显然应该是在第二行出错了，再根据logcat上的日志：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">This Activity already has an action bar supplied by the window decor. </span><br><span class="line">Do not request Window.FEATURE_SUPPORT_ACTION_BAR and </span><br><span class="line">set windowActionBar to false in your theme to use a Toolbar instead.</span><br></pre></td></tr></table></figure></p><p>所以应当是与activity中的ActionBar有冲突，最后尝试多次知道，是要在清单文件中设置activity的style<br>（只要有用到这一行代码的activity都需要在该activity节点下定义style），可以按照如下设置<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">".MainActivity"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:label</span>=<span class="string">"test"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:theme</span>=<span class="string">"@style/MaterialDrawerTheme.Light.DarkToolbar"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.MAIN"</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">                <span class="tag">&lt;<span class="name">category</span> <span class="attr">android:name</span>=<span class="string">"android.intent.category.LAUNCHER"</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">activity</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>这里指的就是第三行了，这样就不会造成程序崩溃了。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近在做一个项目，使用了第三方的开源项目，主要是想实现android5.0之后推出的MaterialDesign的风格，但是代码已经写好了，发现一运行就闪退，所以就开始debug，发现问题出现在&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;setSupportActionBar(toolbar);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="toolbar导致程序崩溃闪退" scheme="http://www.wensibo.top/tags/toolbar%E5%AF%BC%E8%87%B4%E7%A8%8B%E5%BA%8F%E5%B4%A9%E6%BA%83%E9%97%AA%E9%80%80/"/>
    
  </entry>
  
  <entry>
    <title>【原创+译文】官方文档中声明的如何创建抽屉导航栏(Navigation Drawer)</title>
    <link href="http://www.wensibo.top/2016/09/03/%E3%80%90%E5%8E%9F%E5%88%9B+%E8%AF%91%E6%96%87%E3%80%91%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3%E4%B8%AD%E5%A3%B0%E6%98%8E%E7%9A%84%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E6%8A%BD%E5%B1%89%E5%AF%BC%E8%88%AA%E6%A0%8F(Navigation%20Drawer)/"/>
    <id>http://www.wensibo.top/2016/09/03/【原创+译文】官方文档中声明的如何创建抽屉导航栏(Navigation Drawer)/</id>
    <published>2016-09-03T03:01:45.000Z</published>
    <updated>2018-10-21T08:26:20.432Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>抽屉式导航栏是显示在屏幕的左边缘，它是应用程序的主导航选项面板。它大部分时间是处于隐藏状态的，但是当用户从屏幕的左边缘挥动手指时它就会显示出来，而在应用程序的顶层，用户触摸操作栏上的应用程序图标也可以将其显示出来。</p><p>本课程介绍在可用的API 支持库下如何实现导航抽屉DrawerLayout。<br><a id="more"></a></p><p>首先我们可以看一下最终的效果图：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201609/927927-20160903105123465-806778297.gif" alt="DrawerLayout效果图" title="">                </div>                <div class="image-caption">DrawerLayout效果图</div>            </figure></p><h2 id="1、创建一个抽屉布局文件（Drawer-Layout）"><a href="#1、创建一个抽屉布局文件（Drawer-Layout）" class="headerlink" title="1、创建一个抽屉布局文件（Drawer Layout）"></a>1、创建一个抽屉布局文件（Drawer Layout）</h2><p>要添加一个抽屉式导航，首先你必须要声明你的用户界面的根布局为DrawerLayout对象。在DrawerLayout里面，添加一个主视图内容的view对象（当抽屉被隐藏的时候显示在屏幕上的 视图）和另外一个包含抽屉导航视图的view对象。</p><blockquote><p>例如，下面的布局采用了包含两个子视图的DrawerLayout：一个是FrameLayout，包含了主要内容（在运行时由填充Fragment），和一个ListView的导航抽屉。</p></blockquote><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">android.support.v4.widget.DrawerLayout</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">"@+id/drawer_layout"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span> &gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 主视图 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/content_frame"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 抽屉视图 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ListView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/left_drawer"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"240dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_gravity</span>=<span class="string">"start"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:background</span>=<span class="string">"#111"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:choiceMode</span>=<span class="string">"singleChoice"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:divider</span>=<span class="string">"@android:color/transparent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:dividerHeight</span>=<span class="string">"0dp"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">android.support.v4.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>这个布局文件演示了一些比较重要的布局特点，如下：</p></blockquote><ul><li>主内容视图（上面的FrameLayout）必须是DrawerLayout布局对象的第一个子view对象，这是因为xml文件意味着是z排序（即空间上的上下排序，也就是说抽屉导航栏应该位于住内容视图的垂直上方）。</li><li>主内容视图的view对象的两个属性：layout_width、layout_height必须是match_parent的，这是因为当抽屉导航栏被隐藏的时候他便是整个UI。</li><li>抽屉视图要指定其宽度的单位为dp，高度与父视图相匹配。抽屉的宽度应该不超过320dp，这样用户可以随时看到主要内容视图的一些部分。</li><li>抽屉视图（ListView）必须用 android:layout_gravity 属性来指定他的水平重力方向。为了支持从右到左（RTL）的语言，应该要指定其属性为”start” ，而不是”left” 。</li></ul><h2 id="2、初始化抽屉列表"><a href="#2、初始化抽屉列表" class="headerlink" title="2、初始化抽屉列表"></a>2、初始化抽屉列表</h2><p>在你的Activity中，首先要做的事之一就是初始化抽屉导航栏中的列表项。至于你要如何做取决于你的应用程序中的内容，但是通常情况下抽屉导航栏都是包含了一个ListView，所以列表应该由Adapter来填充（如ArrayAdapter或者SimpleCursorAdapter）。</p><blockquote><p>例如下面将告诉你如何用字符串数组初始化抽屉导航栏列表：</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String[] mPlanetTitles;</span><br><span class="line">    <span class="keyword">private</span> DrawerLayout mDrawerLayout;</span><br><span class="line">    <span class="keyword">private</span> ListView mDrawerList;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line">        mPlanetTitles = getResources().getStringArray(R.array.planets_array);</span><br><span class="line">        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);</span><br><span class="line">        mDrawerList = (ListView) findViewById(R.id.left_drawer);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Set the adapter for the list view</span></span><br><span class="line">        mDrawerList.setAdapter(<span class="keyword">new</span> ArrayAdapter&lt;String&gt;(<span class="keyword">this</span>,</span><br><span class="line">                R.layout.drawer_list_item, mPlanetTitles));</span><br><span class="line">        <span class="comment">// Set the list's click listener</span></span><br><span class="line">        mDrawerList.setOnItemClickListener(<span class="keyword">new</span> DrawerItemClickListener());</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>此代码还调用setOnItemClickListener()来接收抽屉导航栏列表的点击事件。下一节将展示如何实现此接口，以实现当用户选择一个项目后更改主内容视图。</p></blockquote><h2 id="3、处理导航的点击事件"><a href="#3、处理导航的点击事件" class="headerlink" title="3、处理导航的点击事件"></a>3、处理导航的点击事件</h2><p>当用户选择在抽屉导航栏里列表中的项目时，系统调用OnItemClickListener接口中的onItemClick()方法以返回给OnItemClickListener() 。你要在onItemClick()方法中做什么样的处理取决于你如何实现你的的应用程序结构。在下面的例子中，你将可以实现下面的内容：当点击抽屉导航栏中列表项里的item时，将会在住内容视图中插入一个不同的Fragment。(FrameLayout的id为 R.id.content_frame)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">DrawerItemClickListener</span> <span class="keyword">implements</span> <span class="title">ListView</span>.<span class="title">OnItemClickListener</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onItemClick</span><span class="params">(AdapterView&lt;?&gt; parent, View view, <span class="keyword">int</span> position, <span class="keyword">long</span> id)</span> </span>&#123;</span><br><span class="line">        selectItem(position);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** Swaps fragments in the main content view */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">selectItem</span><span class="params">(<span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Create a new fragment and specify the planet to show based on position</span></span><br><span class="line">    Fragment fragment = <span class="keyword">new</span> PlanetFragment();</span><br><span class="line">    Bundle args = <span class="keyword">new</span> Bundle();</span><br><span class="line">    args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);</span><br><span class="line">    fragment.setArguments(args);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Insert the fragment by replacing any existing fragment</span></span><br><span class="line">    FragmentManager fragmentManager = getFragmentManager();</span><br><span class="line">    fragmentManager.beginTransaction()</span><br><span class="line">                   .replace(R.id.content_frame, fragment)</span><br><span class="line">                   .commit();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Highlight the selected item, update the title, and close the drawer</span></span><br><span class="line">    mDrawerList.setItemChecked(position, <span class="keyword">true</span>);</span><br><span class="line">    setTitle(mPlanetTitles[position]);</span><br><span class="line">    mDrawerLayout.closeDrawer(mDrawerList);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTitle</span><span class="params">(CharSequence title)</span> </span>&#123;</span><br><span class="line">    mTitle = title;</span><br><span class="line">    getActionBar().setTitle(mTitle);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="4、监听打开以及关闭抽屉导航栏的事件"><a href="#4、监听打开以及关闭抽屉导航栏的事件" class="headerlink" title="4、监听打开以及关闭抽屉导航栏的事件"></a>4、监听打开以及关闭抽屉导航栏的事件</h2><p>要监听抽屉打开和关闭事件，在你的DrawerLayout中调用setDrawerListener()并传入一个DrawerLayout.DrawerListener接口 。此接口为抽屉导航栏提供了回掉方法，如onDrawerOpened()和onDrawerClosed()。</p><p>然而，除了实现DrawerLayout.DrawerListener接口之外，如果你的Activity包含了ActionBar，你也可以继承ActionBarDrawerToggle这个类，这是因为ActionBarDrawerToggle类实现DrawerLayout.DrawerListener接口，所以你仍然可以复写这些方法，但是这对ActionBar图标和抽屉的交互也有一定的帮助。</p><p>正如在抽屉导航设计指南讨论的一样，当抽屉处于可见状态时你应该要修改操作栏（ActionBar）的内容，例如你应该要改变标题，并移除与主内容试图相关的列表项。下面的代码演示了如何通过实例化ActionBarDrawerToggle类并复写在DrawerLayout.DrawerListener中的回调方法，以达到这样的目的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> DrawerLayout mDrawerLayout;</span><br><span class="line">    <span class="keyword">private</span> ActionBarDrawerToggle mDrawerToggle;</span><br><span class="line">    <span class="keyword">private</span> CharSequence mDrawerTitle;</span><br><span class="line">    <span class="keyword">private</span> CharSequence mTitle;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        mTitle = mDrawerTitle = getTitle();</span><br><span class="line">        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);</span><br><span class="line">        mDrawerToggle = <span class="keyword">new</span> ActionBarDrawerToggle(<span class="keyword">this</span>, mDrawerLayout,</span><br><span class="line">                R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/** Called when a drawer has settled in a completely closed state. */</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDrawerClosed</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onDrawerClosed(view);</span><br><span class="line">                getActionBar().setTitle(mTitle);</span><br><span class="line">                invalidateOptionsMenu(); <span class="comment">// creates call to onPrepareOptionsMenu()</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/** Called when a drawer has settled in a completely open state. */</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDrawerOpened</span><span class="params">(View drawerView)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onDrawerOpened(drawerView);</span><br><span class="line">                getActionBar().setTitle(mDrawerTitle);</span><br><span class="line">                invalidateOptionsMenu(); <span class="comment">// creates call to onPrepareOptionsMenu()</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Set the drawer toggle as the DrawerListener</span></span><br><span class="line">        mDrawerLayout.setDrawerListener(mDrawerToggle);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Called whenever we call invalidateOptionsMenu() */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onPrepareOptionsMenu</span><span class="params">(Menu menu)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// If the nav drawer is open, hide action items related to the content view</span></span><br><span class="line">        <span class="keyword">boolean</span> drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);</span><br><span class="line">        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.onPrepareOptionsMenu(menu);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面将介绍ActionBarDrawerToggle构造函数的参数，并设置它来处理与操作栏图标交互所需的其他步骤。</p><h2 id="5、如何通过应用程序图标打开或关闭抽屉导航栏"><a href="#5、如何通过应用程序图标打开或关闭抽屉导航栏" class="headerlink" title="5、如何通过应用程序图标打开或关闭抽屉导航栏"></a>5、如何通过应用程序图标打开或关闭抽屉导航栏</h2><p>用户可以通过来自或朝向屏幕的左边缘轻扫的手势来打开与关闭抽屉式导航栏，但如果你正在使用操作栏（ActionBar） ，你也应该允许用户通过触摸应用程序图标的方式打开或者关闭抽屉导航栏。而应用程序图标也应用一个特殊的图标注明抽屉的存在。你也可以通过实现上一节讲到的ActionBarDrawerToggle来实现这些行为。</p><p>为了使ActionBarDrawerToggle发挥作用，你将需要创建它的一个实例与它的构造方法，这需要下列参数：　</p><ul><li>持有该抽屉的Activity</li><li>一个DrawerLayout</li><li>用于作为抽屉指标的绘制资源（drawable resource），这里会提供一个android官方提供的<a href="http://files.cnblogs.com/files/ghylzwsb/Android_Design_Icons_20130926.zip" target="_blank" rel="noopener">图标包</a>。</li><li>用来形容“打开抽屉”这一操作的字符串资源</li><li>用来形容“关闭抽屉”这一操作的字符串资源</li></ul><p>最后，不管你是否已经创建了ActionBarDrawerToggle的子类作为抽屉的监听器，你还是需要在整个Activity的生命周期中的几个地方调用ActionBarDrawerToggle：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> DrawerLayout mDrawerLayout;</span><br><span class="line">    <span class="keyword">private</span> ActionBarDrawerToggle mDrawerToggle;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);</span><br><span class="line">        mDrawerToggle = <span class="keyword">new</span> ActionBarDrawerToggle(</span><br><span class="line">                <span class="keyword">this</span>,                  <span class="comment">/* host Activity */</span></span><br><span class="line">                mDrawerLayout,         <span class="comment">/* DrawerLayout object */</span></span><br><span class="line">                R.drawable.ic_drawer,  <span class="comment">/* nav drawer icon to replace 'Up' caret */</span></span><br><span class="line">                R.string.drawer_open,  <span class="comment">/* "open drawer" description */</span></span><br><span class="line">                R.string.drawer_close  <span class="comment">/* "close drawer" description */</span></span><br><span class="line">                ) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/** Called when a drawer has settled in a completely closed state. */</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDrawerClosed</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onDrawerClosed(view);</span><br><span class="line">                getActionBar().setTitle(mTitle);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/** Called when a drawer has settled in a completely open state. */</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDrawerOpened</span><span class="params">(View drawerView)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">super</span>.onDrawerOpened(drawerView);</span><br><span class="line">                getActionBar().setTitle(mDrawerTitle);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Set the drawer toggle as the DrawerListener</span></span><br><span class="line">        mDrawerLayout.setDrawerListener(mDrawerToggle);</span><br><span class="line"></span><br><span class="line">        getActionBar().setDisplayHomeAsUpEnabled(<span class="keyword">true</span>);</span><br><span class="line">        getActionBar().setHomeButtonEnabled(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onPostCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onPostCreate(savedInstanceState);</span><br><span class="line">        <span class="comment">// Sync the toggle state after onRestoreInstanceState has occurred.</span></span><br><span class="line">        mDrawerToggle.syncState();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onConfigurationChanged</span><span class="params">(Configuration newConfig)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onConfigurationChanged(newConfig);</span><br><span class="line">        mDrawerToggle.onConfigurationChanged(newConfig);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onOptionsItemSelected</span><span class="params">(MenuItem item)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Pass the event to ActionBarDrawerToggle, if it returns</span></span><br><span class="line">        <span class="comment">// true, then it has handled the app icon touch event</span></span><br><span class="line">        <span class="keyword">if</span> (mDrawerToggle.onOptionsItemSelected(item)) &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// Handle your other action bar items...</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.onOptionsItemSelected(item);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>教程的最后，我提供了项目供你<a href="http://files.cnblogs.com/files/ghylzwsb/DrawerLayout_sample.rar" target="_blank" rel="noopener">下载</a>，希望这个教程能够帮到你。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;抽屉式导航栏是显示在屏幕的左边缘，它是应用程序的主导航选项面板。它大部分时间是处于隐藏状态的，但是当用户从屏幕的左边缘挥动手指时它就会显示出来，而在应用程序的顶层，用户触摸操作栏上的应用程序图标也可以将其显示出来。&lt;/p&gt;
&lt;p&gt;本课程介绍在可用的API 支持库下如何实现导航抽屉DrawerLayout。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="Navigation Drawer" scheme="http://www.wensibo.top/tags/Navigation-Drawer/"/>
    
  </entry>
  
  <entry>
    <title>如何避免Activity 被杀死</title>
    <link href="http://www.wensibo.top/2016/08/13/%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8DActivity%20%E8%A2%AB%E6%9D%80%E6%AD%BB/"/>
    <id>http://www.wensibo.top/2016/08/13/如何避免Activity 被杀死/</id>
    <published>2016-08-13T06:15:05.000Z</published>
    <updated>2018-10-21T08:26:21.104Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>我们都知道，在android系统中，内存不足的时候，系统是可以杀死任何暂停、停止或者销毁的Activity。这就意味着基本上没有在前台的Activity都会面临被关闭的可能。<br><a id="more"></a></p><p>Android系统之所以采用这个机制，而不是像ios系统一样采用墓碑式的管理方式，是因为这样可以在一定程度上加快应用的响应速度，但是由于以前的android手机的性能比较落后，手机运行内存RAM基本上处于2G以内，所以就会导致一些不在前台的Activity有可能被回收，但是现在的智能手机性能已经足够强悍，拿我现在的这部手机——一加手机3（6GRAM）来说，但是之前官方表示为了手机的续航，将内存机制进行修改，使得当用户打开的应用超过一定数量的时候，系统就会对一些Activity进行回收，所以重新打开这些Activity的时候，就会出现重新加载的情况，这在打开大型游戏的时候比较常见。</p><p>那我们在开发的时候如何在系统资源吃紧的时候将一些必要的数据进行及时的存储保护，避免数据的丢失呢？</p><p>我们来看官方API中给出的一个关于Activity的生命周期的图解：<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201608/927927-20160813141237281-477502318.png" alt="Activity生命周期" title="">                </div>                <div class="image-caption">Activity生命周期</div>            </figure></p><p>如果Activity在onPause()后被杀掉，那么onStop()和onDestroy()方法就不会被调用，所以如果在onPause()方法内更多的释放Activity资源，就会让Activity在后台被系统杀掉的几率降低。</p><p>但是杀死一个Activity并不会导致它从Activity堆栈中移除，相反如果Activity实现并使用了onSaveInstanceState()方法用来自定义的保存数据，那么Activity的状态就会被保存到Bundle中，这样当Activity被重新返回的时候，onCreate()方法会被再次调用，这样刚才保存有数据的Bundle对象就会被调用，将数据重新读取；但是该方法并不能保证在任何时候都能被调用。因此我们应该在onPause()方法中保存重要的数据到永久存储(即手机硬件内存中)而用onSaveInstanceState()来保存一些可以从当前屏幕快速恢复的数据(相对不重要的数据)。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我们都知道，在android系统中，内存不足的时候，系统是可以杀死任何暂停、停止或者销毁的Activity。这就意味着基本上没有在前台的Activity都会面临被关闭的可能。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="避免Activity 被杀死" scheme="http://www.wensibo.top/tags/%E9%81%BF%E5%85%8DActivity-%E8%A2%AB%E6%9D%80%E6%AD%BB/"/>
    
  </entry>
  
  <entry>
    <title>SharePreference是如何实现的——序列化XML文件</title>
    <link href="http://www.wensibo.top/2016/04/29/SharePreference%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%9A%84%E2%80%94%E2%80%94%E5%BA%8F%E5%88%97%E5%8C%96XML%E6%96%87%E4%BB%B6/"/>
    <id>http://www.wensibo.top/2016/04/29/SharePreference是如何实现的——序列化XML文件/</id>
    <published>2016-04-29T15:01:45.000Z</published>
    <updated>2018-10-21T08:26:20.952Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>还记得上一篇我们讲到了用SharePreference来存储数据，那么究竟它是如何实现的呢，今天我们就来仔细看看其实现的细节，我们给它一个准确的名字，叫做XML序列化器(XmlSerializer)。</p><p>不同于上面一篇的保存用户的登录名以及密码，这次我们保存设备中的信息，但是由于现在知识有限，我还不能够实现对设备中信息的读取，那么我就在程序中自己生成若干条信息，对这些生成的信息进行读取，并保存到位于SD卡的backup.xml文件中。在这里我是用两种方法对其进行存储并比较两种方法的优缺点，当然作为开发，我更建议使用待会讲到的第二种方法。</p><a id="more"></a><p>先来看看我们需要做到什么样的效果:<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201604/927927-20160429221918082-1162611150.png" alt="存储数据效果图" title="">                </div>                <div class="image-caption">存储数据效果图</div>            </figure></p><h2 id="1、使用StringBuffer，将所有的内容逐一追加到该字符流中。"><a href="#1、使用StringBuffer，将所有的内容逐一追加到该字符流中。" class="headerlink" title="1、使用StringBuffer，将所有的内容逐一追加到该字符流中。"></a>1、使用StringBuffer，将所有的内容逐一追加到该字符流中。</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">backUpSms1</span><span class="params">(View view)</span></span>&#123;</span><br><span class="line">        StringBuffer sb=<span class="keyword">new</span> StringBuffer();</span><br><span class="line">        sb.append(<span class="string">"&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt;"</span>);</span><br><span class="line">        sb.append(<span class="string">"&lt;smss&gt;"</span>);</span><br><span class="line">        <span class="keyword">for</span> (SmsInfo smsinfo: smsInfo) &#123;</span><br><span class="line">            sb.append(<span class="string">"&lt;sms&gt;"</span>);</span><br><span class="line">            sb.append(<span class="string">"&lt;address&gt;"</span>);</span><br><span class="line">            sb.append(smsinfo.getAddress());</span><br><span class="line">            sb.append(<span class="string">"&lt;/address&gt;"</span>);</span><br><span class="line"></span><br><span class="line">            sb.append(<span class="string">"&lt;date&gt;"</span>);</span><br><span class="line">            sb.append(smsinfo.getDate());</span><br><span class="line">            sb.append(<span class="string">"&lt;/date&gt;"</span>);</span><br><span class="line"></span><br><span class="line">            sb.append(<span class="string">"&lt;content&gt;"</span>);</span><br><span class="line">            sb.append(smsinfo.getContent());</span><br><span class="line">            sb.append(<span class="string">"&lt;/content&gt;"</span>);</span><br><span class="line"></span><br><span class="line">            sb.append(<span class="string">"&lt;type&gt;"</span>);</span><br><span class="line">            sb.append(smsinfo.getType());</span><br><span class="line">            sb.append(<span class="string">"&lt;/type&gt;"</span>);</span><br><span class="line">            sb.append(<span class="string">"&lt;/sms&gt;"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        sb.append(<span class="string">"&lt;/smss&gt;"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            File file =<span class="keyword">new</span> File(Environment.getExternalStorageDirectory(), <span class="string">"backUp1.xml"</span>);</span><br><span class="line">            FileOutputStream fos =<span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line">            fos.write(sb.toString().getBytes());</span><br><span class="line">            fos.close();</span><br><span class="line">            Toast.makeText( <span class="keyword">this</span>, <span class="string">"备份成功"</span>,<span class="number">0</span>).show();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            Toast.makeText( <span class="keyword">this</span>, <span class="string">"备份失败"</span>,<span class="number">0</span>).show();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>第一种方法相当简单，只是用了一个字符流，将所有的内容逐一追加就行了，可是当我们的短信中出现了一些比较特殊的字符，例如:”&lt;”或者是”&gt;”那么在读取并写入到xml文件的时候就会出错，这个时候打开该xml文件的时候将会报错；另外当我们需要在sms标签中加入一些属性，如图一所示，那么这个时候仅仅利用字符流来写就会变得冗杂，而且逻辑关系就不严谨了，所以这个时候我们就需要用到第二种方法了。</p><h2 id="2、XML序列化器-使用XmlSerializer编辑xml文件-【推荐】"><a href="#2、XML序列化器-使用XmlSerializer编辑xml文件-【推荐】" class="headerlink" title="2、XML序列化器(使用XmlSerializer编辑xml文件)【推荐】"></a>2、XML序列化器(使用XmlSerializer编辑xml文件)【推荐】</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">backUpSms2</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//初始化序列号器，指定xml写入到哪个文件并指定写入的编码格式</span></span><br><span class="line">            XmlSerializer xmlSerializer =Xml.newSerializer();</span><br><span class="line">            File file =<span class="keyword">new</span> File(Environment.getExternalStorageDirectory(), <span class="string">"backUp2.xml"</span>);</span><br><span class="line">            FileOutputStream fos=<span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line">            xmlSerializer.setOutput(fos,<span class="string">"utf-8"</span>);</span><br><span class="line">            </span><br><span class="line">            xmlSerializer.startDocument(<span class="string">"utf-8"</span>, <span class="keyword">true</span>);<span class="comment">//开始声明文件</span></span><br><span class="line">            xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"smss"</span>);<span class="comment">//开始最外层标签</span></span><br><span class="line">            <span class="keyword">for</span> (SmsInfo sms : smsInfo) &#123;</span><br><span class="line">                xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"sms"</span>);<span class="comment">//写入第一个标签</span></span><br><span class="line">                xmlSerializer.attribute(<span class="keyword">null</span>, <span class="string">"id"</span>, sms.getId()+<span class="string">""</span>);<span class="comment">//第一个标签的属性</span></span><br><span class="line"></span><br><span class="line">                xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"address"</span>);<span class="comment">//写入内一层的标签</span></span><br><span class="line">                xmlSerializer.text(sms.getAddress());</span><br><span class="line">                xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"address"</span>);</span><br><span class="line"></span><br><span class="line">                xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"date"</span>);<span class="comment">//写入内一层的标签</span></span><br><span class="line">                xmlSerializer.text(sms.getDate()+<span class="string">""</span>);</span><br><span class="line">                xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"date"</span>);</span><br><span class="line"></span><br><span class="line">                xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"content"</span>);<span class="comment">//写入内一层的标签</span></span><br><span class="line">                xmlSerializer.text(sms.getContent());</span><br><span class="line">                xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"content"</span>);</span><br><span class="line"></span><br><span class="line">                xmlSerializer.startTag(<span class="keyword">null</span>, <span class="string">"type"</span>);<span class="comment">//写入内一层的标签</span></span><br><span class="line">                xmlSerializer.text(sms.getType()+<span class="string">""</span>);</span><br><span class="line">                xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"type"</span>);</span><br><span class="line"></span><br><span class="line">                xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"sms"</span>);<span class="comment">//结束标签</span></span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            xmlSerializer.endTag(<span class="keyword">null</span>, <span class="string">"smss"</span>);<span class="comment">//结束最外层标签</span></span><br><span class="line">            xmlSerializer.endDocument();<span class="comment">//结束声明文件</span></span><br><span class="line">            fos.close();<span class="comment">//记得一定要关闭输出流</span></span><br><span class="line">            Toast.makeText( <span class="keyword">this</span>, <span class="string">"备份成功"</span>,<span class="number">0</span>).show();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            Toast.makeText( <span class="keyword">this</span>, <span class="string">"备份失败"</span>,<span class="number">0</span>).show();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><blockquote><p>使用XmlSerializer的时候有几个步骤：<br>1、首先需要找到输出流，即通过setOutput方法将输出流以及编码格式传入；<br>2、接着需要声明文件以及结束声明，这是通过startDocument以及endDocument这两个方法来实现的；<br>3、接着就可以通过startTag以及startTag方法来声明标签以及结束标签，要声明标签的内容的时候可以通过text方法，当然这个方法只能允许传入String类型，所以对于其他数据类型，需要先对其进行转换；<br>4、最后一点就是上面讲到的——要在标签中添加属性，那就需要通过attribute方法声明id属性。<br>通过以上的讲解，我们可以得到以下的xml文件：</p></blockquote><pre><code class="xml"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8" standalone="true"?&gt;</span>-<span class="tag">&lt;<span class="name">smss</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"0"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000000<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202224<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：0<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"1"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000001<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：1<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"2"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000002<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：2<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"3"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000003<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：3<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"4"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000004<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：4<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"5"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000005<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：5<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"6"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000006<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：6<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"7"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000007<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：7<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"8"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000008<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：8<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"9"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000009<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：9<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"10"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000010<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：10<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"11"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000011<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：11<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"12"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000012<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：12<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"13"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000013<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：13<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"14"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000014<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：14<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"15"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000015<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：15<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"16"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000016<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：16<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"17"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000017<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：17<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"18"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000018<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：18<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>2<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span>-<span class="tag">&lt;<span class="name">sms</span> <span class="attr">id</span>=<span class="string">"19"</span>&gt;</span><span class="tag">&lt;<span class="name">address</span>&gt;</span>135000000019<span class="tag">&lt;/<span class="name">address</span>&gt;</span><span class="tag">&lt;<span class="name">date</span>&gt;</span>1461845202225<span class="tag">&lt;/<span class="name">date</span>&gt;</span><span class="tag">&lt;<span class="name">content</span>&gt;</span>内容为：19<span class="tag">&lt;/<span class="name">content</span>&gt;</span><span class="tag">&lt;<span class="name">type</span>&gt;</span>1<span class="tag">&lt;/<span class="name">type</span>&gt;</span><span class="tag">&lt;/<span class="name">sms</span>&gt;</span><span class="tag">&lt;/<span class="name">smss</span>&gt;</span></code></pre>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;还记得上一篇我们讲到了用SharePreference来存储数据，那么究竟它是如何实现的呢，今天我们就来仔细看看其实现的细节，我们给它一个准确的名字，叫做XML序列化器(XmlSerializer)。&lt;/p&gt;
&lt;p&gt;不同于上面一篇的保存用户的登录名以及密码，这次我们保存设备中的信息，但是由于现在知识有限，我还不能够实现对设备中信息的读取，那么我就在程序中自己生成若干条信息，对这些生成的信息进行读取，并保存到位于SD卡的backup.xml文件中。在这里我是用两种方法对其进行存储并比较两种方法的优缺点，当然作为开发，我更建议使用待会讲到的第二种方法。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="SharedPreferences" scheme="http://www.wensibo.top/tags/SharedPreferences/"/>
    
  </entry>
  
  <entry>
    <title>使用 SharedPreferences 实现数据的存储和读取</title>
    <link href="http://www.wensibo.top/2016/04/16/%E4%BD%BF%E7%94%A8%20SharedPreferences%20%E5%AE%9E%E7%8E%B0%E6%95%B0%E6%8D%AE%E7%9A%84%E5%AD%98%E5%82%A8%E5%92%8C%E8%AF%BB%E5%8F%96/"/>
    <id>http://www.wensibo.top/2016/04/16/使用 SharedPreferences 实现数据的存储和读取/</id>
    <published>2016-04-16T08:21:04.000Z</published>
    <updated>2018-10-21T08:26:21.168Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>在开发的过程中我们必须遇到的就是如何对用户的数据进行有效的存储以及读取。我们举个例子，现在我们使用app，当我们登陆一个账号的时候选择记住密码软件就会记住我们的账号以及密码，我们退出当前账号，就可以直接点击登陆进入账号内部，而不需要再输入账号和密码了。那么这就是今天我们要说的，如何对用户输入的账号以及密码进行存储，并且进行显示。这里我们使用到了google工程师向我们推荐的一个API——SharedPreferences。<br><a id="more"></a></p><h2 id="关于SharedPreferences"><a href="#关于SharedPreferences" class="headerlink" title="关于SharedPreferences"></a>关于SharedPreferences</h2><p>它是android.content下的一个接口，官方对他的描述是这样的：用于访问和修改getSharedPreferences(String, int)返回偏好设置数据(preference data)的一个接口。对于任何一组特殊的preferences，所有的客户端共享一个此类单独的实例。修改Preferences必须通过一个SharedPreferences.Editor对象，以确保当他们提交存储数据的操作时，preference值保持一致的状态。最后再提交数据的时候必须使用commit()方法将数据完全写入。</p><p>它的优点在于会对一些特殊的字符进行分辨，达到准确读取的目的，传统上我们选择以特殊分隔符来分割要存储的数据，这样虽然会简单点，但是如果用户舌设置的数据同样包含这个分隔符，那么将造成读取失误。</p><h2 id="关于SharedPreferences的实例"><a href="#关于SharedPreferences的实例" class="headerlink" title="关于SharedPreferences的实例"></a>关于SharedPreferences的实例</h2><blockquote><p>这里我只介绍关键代码，整个工程可以在<a href="http://files.cnblogs.com/files/ghylzwsb/shareprefenrence.rar" target="_blank" rel="noopener">该链接</a>下载。</p></blockquote><h3 id="使用saveUserInfo-方法来存储用户的数据："><a href="#使用saveUserInfo-方法来存储用户的数据：" class="headerlink" title="使用saveUserInfo()方法来存储用户的数据："></a>使用saveUserInfo()方法来存储用户的数据：</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line">     * 保存用户名 密码的业务方法</span><br><span class="line">     * @param context 上下文</span><br><span class="line">     * @param username 用户名</span><br><span class="line">     * @param pas 密码</span><br><span class="line">     * @return true 保存成功  false 保存失败</span><br><span class="line">     */</span><br><span class="line">    public static void saveUserInfo(Context context,String username,String pas)&#123;</span><br><span class="line">        /**</span><br><span class="line">         * SharedPreferences将用户的数据存储到该包下的shared_prefs/config.xml文件中，</span><br><span class="line">         * 并且设置该文件的读取方式为私有，即只有该软件自身可以访问该文件</span><br><span class="line">         */</span><br><span class="line">        SharedPreferences sPreferences=context.getSharedPreferences(&quot;config&quot;, context.MODE_PRIVATE);</span><br><span class="line">        Editor editor=sPreferences.edit();</span><br><span class="line">        //当然sharepreference会对一些特殊的字符进行转义，使得读取的时候更加准确</span><br><span class="line">        editor.putString(&quot;username&quot;, username);</span><br><span class="line">        editor.putString(&quot;password&quot;, pas);</span><br><span class="line">        //这里我们输入一些特殊的字符来实验效果</span><br><span class="line">        editor.putString(&quot;specialtext&quot;, &quot;hajsdh&gt;&lt;?//&quot;);</span><br><span class="line">        editor.putBoolean(&quot;or&quot;, true);</span><br><span class="line">        editor.putInt(&quot;int&quot;, 47);</span><br><span class="line">        //切记最后要使用commit方法将数据写入文件</span><br><span class="line">        editor.commit();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="当用户重新打开软件时使用如下代码将其显示出来："><a href="#当用户重新打开软件时使用如下代码将其显示出来：" class="headerlink" title="当用户重新打开软件时使用如下代码将其显示出来："></a>当用户重新打开软件时使用如下代码将其显示出来：</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">　　  <span class="comment">//显示用户此前录入的数据</span></span><br><span class="line">SharedPreferences sPreferences=getSharedPreferences(<span class="string">"config"</span>, MODE_PRIVATE);</span><br><span class="line">String username=sPreferences.getString(<span class="string">"username"</span>, <span class="string">""</span>);</span><br><span class="line">String password =sPreferences.getString(<span class="string">"password"</span>, <span class="string">""</span>);</span><br><span class="line">ed_username.setText(username);</span><br><span class="line">ed_pasw.setText(password);</span><br></pre></td></tr></table></figure><h3 id="软件运行之后我们可以发现config-xml文件的内容如下："><a href="#软件运行之后我们可以发现config-xml文件的内容如下：" class="headerlink" title="软件运行之后我们可以发现config.xml文件的内容如下："></a>软件运行之后我们可以发现config.xml文件的内容如下：</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">map</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">string</span> <span class="attr">name</span>=<span class="string">"specialtext"</span>&gt;</span>hajsdh&amp;gt;&amp;lt;?//<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">string</span> <span class="attr">name</span>=<span class="string">"username"</span>&gt;</span>dsa<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">string</span> <span class="attr">name</span>=<span class="string">"password"</span>&gt;</span>dasdasd<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">int</span> <span class="attr">name</span>=<span class="string">"int"</span> <span class="attr">value</span>=<span class="string">"47"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">boolean</span> <span class="attr">name</span>=<span class="string">"or"</span> <span class="attr">value</span>=<span class="string">"true"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">map</span>&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>我们看到，在xml文件中“&gt;&lt;”被转以为“&gt;&lt;”，这也为数据的准确读取做好了规范。</p></blockquote><h3 id="运行截图："><a href="#运行截图：" class="headerlink" title="运行截图："></a>运行截图：</h3><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201604/927927-20160416212554504-89357901.png" alt="运行截图" title="">                </div>                <div class="image-caption">运行截图</div>            </figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>至此SharedPreferences的应用结束。在以后的开发过程中，应当多家注意使用官方推荐的API，这会对数据的安全以及数据的完整性有所保障。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在开发的过程中我们必须遇到的就是如何对用户的数据进行有效的存储以及读取。我们举个例子，现在我们使用app，当我们登陆一个账号的时候选择记住密码软件就会记住我们的账号以及密码，我们退出当前账号，就可以直接点击登陆进入账号内部，而不需要再输入账号和密码了。那么这就是今天我们要说的，如何对用户输入的账号以及密码进行存储，并且进行显示。这里我们使用到了google工程师向我们推荐的一个API——SharedPreferences。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="SharedPreferences" scheme="http://www.wensibo.top/tags/SharedPreferences/"/>
    
  </entry>
  
  <entry>
    <title>如何实现微信5.0的滑动效果</title>
    <link href="http://www.wensibo.top/2016/04/10/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%BE%AE%E4%BF%A15.0%E7%9A%84%E6%BB%91%E5%8A%A8%E6%95%88%E6%9E%9C/"/>
    <id>http://www.wensibo.top/2016/04/10/如何实现微信5.0的滑动效果/</id>
    <published>2016-04-10T08:21:04.000Z</published>
    <updated>2018-10-21T08:26:21.136Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>作为初学者，虽然深知不可一步登天也应当脚踏实地，但是总会有一些奇思异想想要去实现。在实现第一个app的时候我们遇到的另一个头疼的问题便是如何实现像微信5.0版本过后的滑动效果，查阅官方文档以及借鉴网上大神的经验之后我们也顺利得到了一点思路。虽然我另外一个伙伴已经实现了此功能，但我总觉得有些许的冗杂，经过研究学习之后现在已经得到了优化，详见下文。<br><a id="more"></a></p><h2 id="1、关于android-support-v4"><a href="#1、关于android-support-v4" class="headerlink" title="1、关于android.support.v4"></a>1、关于android.support.v4</h2><p>google提供了Android Support Library package 系列的包来保证来高版本sdk开发的向下兼容性，其中v4包是为了照顾1.6及更高版本而设计的，这个包是使用最广泛的，eclipse新建工程时，都默认带有了。但是我一开始在开发中一直指不到该包的源代码，一番搜索之后找到了解决方法：1、首先为工程build path，找到路径：android-sdk-windows\extras\android\support\v4\android-support-v4.jar；2、导入该包并不能正确的找到源代码，需要在java bulid path –&gt;Order and Export下找到刚刚导入的support\v4\android-support-v4.jar，并将其选择到top位置；3、点击apply之后就可以顺利的查到源代码。</p><p>该包下比较常用的类有Fragment以及ViewPager，我的伙伴在实现该功能时使用的是Fragment+ViewPager，这也是官方一直推荐的，但是因为觉得有些许的冗杂，所以这里我只是用ViewPager来实现。关于该包下的这两个常用的类以及其他关键的UI效果的类大家可以查看文档了解，这里就不赘述。</p><h2 id="2、具体实现"><a href="#2、具体实现" class="headerlink" title="2、具体实现"></a>2、具体实现</h2><p>为了美观，这里只显示关键代码，工程的整体代码可以到<a href="http://120.198.244.55:9999/files.cnblogs.com/files/ghylzwsb/scroll.rar" target="_blank" rel="noopener">我的博客中下载</a>：</p><ul><li>MainActivity关键代码</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">instantiateItem</span><span class="params">(ViewGroup container, <span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (mViewList.get(position).getParent() != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    container.removeView(mViewList.get(position));</span><br><span class="line">                &#125;</span><br><span class="line">                container.addView(mViewList.get(position));</span><br><span class="line"></span><br><span class="line">                <span class="keyword">return</span> mViewList.get(position);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroyItem</span><span class="params">(ViewGroup container, <span class="keyword">int</span> position, Object object)</span> </span>&#123;</span><br><span class="line">                container.removeView(mViewList.get(position));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> mViewList.size();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        mViewPager.setOnPageChangeListener(<span class="keyword">new</span> OnPageChangeListener() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPageScrolled</span><span class="params">(<span class="keyword">int</span> position, <span class="keyword">float</span> positionOffset, <span class="keyword">int</span> positionOffsetPixels)</span></span>&#123;</span><br><span class="line">                <span class="keyword">int</span> x = (<span class="keyword">int</span>)((position + positionOffset) * mTextView.getWidth());</span><br><span class="line">                ((View)mTextView.getParent()).scrollTo(-x, mTextView.getScrollY());                </span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPageSelected</span><span class="params">(<span class="keyword">int</span> position)</span></span>&#123;</span><br><span class="line">                <span class="keyword">if</span>(position == <span class="number">0</span>)&#123;</span><br><span class="line">                    radioButton1.setChecked(<span class="keyword">true</span>);</span><br><span class="line">                &#125;<span class="keyword">else</span> <span class="keyword">if</span>(position == <span class="number">1</span>)&#123;</span><br><span class="line">                    radioButton2.setChecked(<span class="keyword">true</span>);</span><br><span class="line">                &#125;<span class="keyword">else</span> <span class="keyword">if</span>(position == <span class="number">2</span>)&#123;</span><br><span class="line">                    radioButton3.setChecked(<span class="keyword">true</span>);</span><br><span class="line">                &#125;<span class="keyword">else</span> <span class="keyword">if</span>(position == <span class="number">3</span>)&#123;                    </span><br><span class="line">                    radioButton4.setChecked(<span class="keyword">true</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPageScrollStateChanged</span><span class="params">(<span class="keyword">int</span> state)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">int</span> tmpState = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">dip2px</span><span class="params">(<span class="keyword">float</span> dipValue)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> scale = getResources().getDisplayMetrics().density;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (dipValue * scale + <span class="number">0.5f</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">px2dip</span><span class="params">(<span class="keyword">float</span> pxValue)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> scale = getResources().getDisplayMetrics().density;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (pxValue / scale + <span class="number">0.5f</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ul><li>activity_main.xml</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;</span><br><span class="line">    xmlns:tools=&quot;http://schemas.android.com/tools&quot;</span><br><span class="line">    android:layout_width=&quot;match_parent&quot;</span><br><span class="line">    android:layout_height=&quot;match_parent&quot;</span><br><span class="line">    android:background=&quot;#FFFFFFFF&quot;</span><br><span class="line">    android:orientation=&quot;vertical&quot; &gt;</span><br><span class="line"></span><br><span class="line">    &lt;RadioGroup</span><br><span class="line">        android:id=&quot;@+id/radioGroup&quot;</span><br><span class="line">        android:layout_width=&quot;match_parent&quot;</span><br><span class="line">        android:layout_height=&quot;wrap_content&quot;</span><br><span class="line">        android:gravity=&quot;center&quot;</span><br><span class="line">        android:orientation=&quot;horizontal&quot; &gt;</span><br><span class="line"></span><br><span class="line">        &lt;RadioButton</span><br><span class="line">            android:id=&quot;@+id/radioButton1&quot;</span><br><span class="line">            style=&quot;@style/radioButtonStyle&quot;</span><br><span class="line">            android:text=&quot;页面1&quot; /&gt;</span><br><span class="line"></span><br><span class="line">        &lt;RadioButton</span><br><span class="line">            android:id=&quot;@+id/radioButton2&quot;</span><br><span class="line">            style=&quot;@style/radioButtonStyle&quot;</span><br><span class="line">            android:text=&quot;页面2&quot; /&gt;</span><br><span class="line"></span><br><span class="line">        &lt;RadioButton</span><br><span class="line">            android:id=&quot;@+id/radioButton3&quot;</span><br><span class="line">            style=&quot;@style/radioButtonStyle&quot;</span><br><span class="line">            android:text=&quot;页面3&quot; /&gt;</span><br><span class="line"></span><br><span class="line">        &lt;RadioButton</span><br><span class="line">            android:id=&quot;@+id/radioButton4&quot;</span><br><span class="line">            style=&quot;@style/radioButtonStyle&quot;</span><br><span class="line">            android:text=&quot;页面4&quot; /&gt;</span><br><span class="line">    &lt;/RadioGroup&gt;</span><br><span class="line"></span><br><span class="line">    &lt;LinearLayout</span><br><span class="line">        android:layout_width=&quot;match_parent&quot;</span><br><span class="line">        android:layout_height=&quot;3dip&quot;</span><br><span class="line">        android:orientation=&quot;horizontal&quot; &gt;</span><br><span class="line"></span><br><span class="line">        &lt;TextView</span><br><span class="line">            android:id=&quot;@+id/textView&quot;</span><br><span class="line">            android:layout_width=&quot;200dip&quot;</span><br><span class="line">            android:layout_height=&quot;3dip&quot;</span><br><span class="line">            android:background=&quot;#FF336699&quot;</span><br><span class="line">            android:textColor=&quot;#FFFF0000&quot; /&gt;</span><br><span class="line">    &lt;/LinearLayout&gt;</span><br><span class="line"></span><br><span class="line">    &lt;View</span><br><span class="line">        android:layout_width=&quot;wrap_content&quot;</span><br><span class="line">        android:layout_height=&quot;1dip&quot;</span><br><span class="line">        android:background=&quot;#FF336699&quot; /&gt;</span><br><span class="line"></span><br><span class="line">    &lt;android.support.v4.view.ViewPager</span><br><span class="line">        android:id=&quot;@+id/viewPager&quot;</span><br><span class="line">        android:layout_width=&quot;match_parent&quot;</span><br><span class="line">        android:layout_height=&quot;match_parent&quot; &gt;</span><br><span class="line">    &lt;/android.support.v4.view.ViewPager&gt;</span><br><span class="line"></span><br><span class="line">&lt;/LinearLayout&gt;</span><br></pre></td></tr></table></figure><h2 id="3、具体效果"><a href="#3、具体效果" class="headerlink" title="3、具体效果"></a>3、具体效果</h2><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201604/927927-20160410161503062-1179134914.gif" alt="运行截图" title="">                </div>                <div class="image-caption">运行截图</div>            </figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;作为初学者，虽然深知不可一步登天也应当脚踏实地，但是总会有一些奇思异想想要去实现。在实现第一个app的时候我们遇到的另一个头疼的问题便是如何实现像微信5.0版本过后的滑动效果，查阅官方文档以及借鉴网上大神的经验之后我们也顺利得到了一点思路。虽然我另外一个伙伴已经实现了此功能，但我总觉得有些许的冗杂，经过研究学习之后现在已经得到了优化，详见下文。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="微信滑动效果" scheme="http://www.wensibo.top/tags/%E5%BE%AE%E4%BF%A1%E6%BB%91%E5%8A%A8%E6%95%88%E6%9E%9C/"/>
    
  </entry>
  
  <entry>
    <title>安卓下如何使用JUnit进行软件测试</title>
    <link href="http://www.wensibo.top/2016/04/04/%E5%AE%89%E5%8D%93%E4%B8%8B%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8JUnit%E8%BF%9B%E8%A1%8C%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/"/>
    <id>http://www.wensibo.top/2016/04/04/安卓下如何使用JUnit进行软件测试/</id>
    <published>2016-04-04T14:56:04.000Z</published>
    <updated>2018-10-21T08:26:21.044Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>软件测试作为程序员必备的一项技能是决定软件开发周期长短以及软件运行成败的关键，可以说好的软件不是代码写得好而是有效的测试决定的。本文将介绍在Android下利用eclipse进行开发时如何使用JUnit进行单元测试。<br><a id="more"></a></p><h2 id="测试的分类（仅举例其中一些方法）"><a href="#测试的分类（仅举例其中一些方法）" class="headerlink" title="测试的分类（仅举例其中一些方法）"></a>测试的分类（仅举例其中一些方法）</h2><h3 id="根据测试是否知道代码"><a href="#根据测试是否知道代码" class="headerlink" title="根据测试是否知道代码"></a>根据测试是否知道代码</h3><blockquote><p>1、黑盒测试(测试的时候不知道具体代码):指的是把被测的软件看作是一个黑盒子，我们不去关心盒子里面的结构是什么样子的，只关心软件的输入数据和输出结果。它只检查程序功能是否按照需求规格说明书的规定正常使用，程序是否能适当地接收输入数据而产生正确的输出信息。黑盒测试着眼于程序外部结构，不考虑内部逻辑结构，主要针对软件界面和软件功能进行测试。 </p></blockquote><blockquote><p>2、白盒测试(测试的时候需要了解具体的代码):指的是把盒子盖子打开，去研究里面的源代码和程序结果。是按照程序内部的结构测试程序，通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行，检验程序中的每条通路是否都能按预定要求正确工作。</p></blockquote><blockquote><p>3、灰盒测试(灰盒测试介于黑盒测试与白盒测试之间):可以这样理解，灰盒测试关注输出对于输入的正确性，同时也关注内部表现，但这种关注不象白盒那样详细、完整，只是通过一些表征性的现象、事件、标志来判断内部的运行状态，有时候输出是正确的，但内部其实已经错误了，这种情况非常多，如果每次都通过白盒测试来操作，效率会很低，因此需要采取这样的一种灰盒的方法。 </p></blockquote><h3 id="根据测试的粒度"><a href="#根据测试的粒度" class="headerlink" title="根据测试的粒度"></a>根据测试的粒度</h3><blockquote><p>1、方法测试(function test)：验证模块的功能。</p></blockquote><blockquote><p>2、单元测试(unit test):在最低的功能/参数上验证程序的准确性,比如测试一个函数的正确性。</p></blockquote><blockquote><p>3、集成测试(intergration test):验证几个互相有依赖关系的模块的功能。</p></blockquote><h3 id="根据测试的次数"><a href="#根据测试的次数" class="headerlink" title="根据测试的次数"></a>根据测试的次数</h3><blockquote><p>1、冒烟测试(smoke test)：指的是测试人员在同一时间对软件进行大量的点击或者功能测试，测试软件遭到这样的压力时是否能够扛得住，关键在于同一个用户在极短的时间内对软件进行大量重复的测试。</p></blockquote><blockquote><p>2、压力测试(pressure test):指的是软件或者网站在同一时间内被大量的用户访问，突出的是软件或者网站被大量客户访问时的抗压能力，一般运用于大型网站的测试。</p></blockquote><h2 id="举例对其中的JUnit测试进行讲解"><a href="#举例对其中的JUnit测试进行讲解" class="headerlink" title="举例对其中的JUnit测试进行讲解"></a>举例对其中的JUnit测试进行讲解</h2><p>这里我们想要对安卓应用程序中AppService类中的randomArray()方法进行单元测试。</p><p><strong>注：试直接使用java的JUnit是无效的，应为java应用程序时在java虚拟机(JVM)运行的，而安卓程序则是在终端的Dalvik虚拟机运行的，所以直接对其进行JUnit测试会报错，故我们可以使用以下方法进行测试。</strong></p><blockquote><p>1、创建一个包，并在包下新建一个测试类(TestService)用来测试该方法，具体代码如下。</p></blockquote><ul><li>TestService类：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.app.wolf;</span><br><span class="line"></span><br><span class="line"><span class="comment">//AppService 的randomArray方法：</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AppService</span> </span>&#123;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 随机指定范围内N个不重复的数 在初始化的无重复待选数组中随机产生一个数放入结果中，</span></span><br><span class="line"><span class="comment">     * 将待选数组被随机到的数，用待选数组(len-1)下标对应的数替换 然后从len-2里随机产生下一个随机数，如此类推</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> max</span></span><br><span class="line"><span class="comment">     *            指定范围最大值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> min</span></span><br><span class="line"><span class="comment">     *            指定范围最小值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> n</span></span><br><span class="line"><span class="comment">     *            随机数个数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> int[] 随机数结果集</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span>[] randomArray(<span class="keyword">int</span> min, <span class="keyword">int</span> max, <span class="keyword">int</span> n) &#123;</span><br><span class="line">        <span class="keyword">int</span> len = max - min + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (max &lt; min || n &gt; len) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 初始化给定范围的待选数组</span></span><br><span class="line">        <span class="keyword">int</span>[] source = <span class="keyword">new</span> <span class="keyword">int</span>[len];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = min; i &lt; min + len; i++) &#123;</span><br><span class="line">            source[i - min] = i;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span>[] result = <span class="keyword">new</span> <span class="keyword">int</span>[n];</span><br><span class="line">        Random rd = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//这个算法666，我理解的步骤应该是这样的</span></span><br><span class="line">        <span class="comment">//1、首先初始化一个数组source，这个数组的长度是用户选择要开始游戏的人数，接着将数组中的元素分别是0~数组长度-1；</span></span><br><span class="line">        <span class="comment">//2、接着借用一个一个随机变量index,这个变量产生的随机数范围是0~数组长度-1；</span></span><br><span class="line">        <span class="comment">//3、最后将每一个index对应的source的内容对应到最后返回的result数组中；</span></span><br><span class="line">        <span class="comment">//最厉害的地方就是首先用index索引取得source数组中的内容，此时数组的长度减1，接着用数组的最后一个元素来代替之，</span></span><br><span class="line">        <span class="comment">//这样就不会出现数组中元素被重复使用的情况了。</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; result.length; i++) &#123;</span><br><span class="line">            <span class="comment">// 待选数组0到(len-2)随机一个下标</span></span><br><span class="line">            index = Math.abs(rd.nextInt() % len--);</span><br><span class="line">            <span class="comment">// 将随机到的数放入结果集</span></span><br><span class="line">            result[i] = source[index];</span><br><span class="line">            <span class="comment">// 将待选数组中被随机到的数，用待选数组(len-1)下标对应的数替换</span></span><br><span class="line">            source[index] = source[len];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i:result)&#123;</span><br><span class="line">            System.out.print(i+<span class="string">"\t"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>TestService类：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.app.wolf.testService;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.app.wolf.AppService;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.R.integer;</span><br><span class="line"><span class="keyword">import</span> android.test.AndroidTestCase;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestService</span> <span class="keyword">extends</span> <span class="title">AndroidTestCase</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用JUnit测试randomArray方法</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testRandomArray</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        AppService service=<span class="keyword">new</span> AppService();</span><br><span class="line">        <span class="keyword">int</span>[] resultArray=service.randomArray(<span class="number">2</span>, <span class="number">7</span>, <span class="number">6</span>);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> result:resultArray)&#123;</span><br><span class="line">            System.out.print(result+<span class="string">"\t"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>2、如果直接在outline中对 testRandomArray()方法右击进行Android JUnit Test ，则会曝出以下异常：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WolfApp does not specify a android.test.InstrumentationTestRunner </span><br><span class="line">instrumentation or does not declare uses-library android.test.runner in its AndroidManifest.xml`</span><br></pre></td></tr></table></figure></p></blockquote><blockquote><p>这是因为在AndroidManifest.xml中没有对 InstrumentationTestRunner 以及 uses-library 进行配置。</p></blockquote><p>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　</p><blockquote><p>3、对于上面的错误，我们可以在AndroidManifest.xml文件加上以下的代码进行配置：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 使用该行语句对instrumentation进行配置，但是值得注意的是targetPackage应该选择你想要测试的方法所在的包 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">instrumentation</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:name</span>=<span class="string">"android.test.InstrumentationTestRunner"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:targetPackage</span>=<span class="string">"com.app.wolf"</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">instrumentation</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 使用改行代码可以对uses-library进行配置，但是必须放在application节点下 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">uses-library</span> <span class="attr">android:name</span>=<span class="string">"android.test.runner"</span> /&gt;</span></span><br></pre></td></tr></table></figure></p></blockquote><blockquote><p>对AndroidManifest.xml进行配置好了之后就回到第2步对该方法进行Android JUnit Test就能够成功执行了。</p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在测试的过程中应该适当地使用断言assert对程序进行测试，这对于检验程序的运行以及检查出错地方具有很有效的用处，但是本测试实验由于输出的为数组，要判断的话应该检查数组中的元素是否为要求的元素，这样检验起来会比较麻烦，所以笔者选择了打印输出的方式进行检验。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;软件测试作为程序员必备的一项技能是决定软件开发周期长短以及软件运行成败的关键，可以说好的软件不是代码写得好而是有效的测试决定的。本文将介绍在Android下利用eclipse进行开发时如何使用JUnit进行单元测试。&lt;br&gt;
    
    </summary>
    
      <category term="Android" scheme="http://www.wensibo.top/categories/Android/"/>
    
    
      <category term="JUnit测试" scheme="http://www.wensibo.top/tags/JUnit%E6%B5%8B%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>将一个数组进行随机再排列</title>
    <link href="http://www.wensibo.top/2016/04/03/%E5%B0%86%E4%B8%80%E4%B8%AA%E6%95%B0%E7%BB%84%E8%BF%9B%E8%A1%8C%E9%9A%8F%E6%9C%BA%E5%86%8D%E6%8E%92%E5%88%97/"/>
    <id>http://www.wensibo.top/2016/04/03/将一个数组进行随机再排列/</id>
    <published>2016-04-03T14:35:04.000Z</published>
    <updated>2018-10-21T08:26:21.072Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>我们在开发第一个app的时候遇到的一个比较有趣的算法，这种将一个数组重新进行随机排序的问题并不罕见，但是因为是初学者，所以在探讨这个算法的过程中也纠结了很久，当然最后的算法也是参考借鉴了且听风吟博主的一篇文章：<a href="http://wsjiang.iteye.com/blog/1775341" target="_blank" rel="noopener">http://wsjiang.iteye.com/blog/1775341</a> ，在此鸣谢！<br><a id="more"></a></p><h2 id="问题的解决："><a href="#问题的解决：" class="headerlink" title="问题的解决："></a>问题的解决：</h2><p>1、假设想要将2~7这6个数字随机排序成一个数组，这里我们设置min为该范围的最小值2，max为该范围的上限7，n为想要在这个范围中取出多少个数字组成一个数组，当然当n等于范围的长度len即6时，那么得到的就是将原来的6个数字重新随机排序一遍。</p><p>2、为了方便我们引进一个初始化数组source，并将刚刚范围中的所有数字依次存进该数组中，则初始化数组source为{2,3,4,5,6,7}。</p><p>3、为了方便我们同样再引进另一个数组result作为最后返回的数组。在这里我们知道一开始len=6，那么就随机取得一个随机数index(0&lt;=index&lt;=len-1),接下来就要在source数组中找到index位置上的元素放入到result数组的第0位，这时应该将len自减，同时在source数组中将source[index]替换为source[len]。</p><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201604/927927-20160403222037551-2125721015.png" alt="流程图" title="">                </div>                <div class="image-caption">流程图</div>            </figure><p>4、以此类推直到数组result已满位置。最终得到的就是一个经过随机排序的数组。</p><p>5、代码如下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestRandom</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 该方法用于在制定范围即（min~max）内对min到max的所有数字进行重新随机排列，使他们形成一组随机数。</span></span><br><span class="line"><span class="comment">     *  1、首先先初始化一个长度为(max-min+1)的数组source，数组的元素按照数字从大到小的顺序分别为min~max；</span></span><br><span class="line"><span class="comment">     *  2、每次取一个随机数index，0&lt;=index&lt;=(数组长度len-1)，同时len自减1，</span></span><br><span class="line"><span class="comment">     *  取出source数组中对应在index位置上的元素依次放进最终数组result，并且将source[index]替换为source[len]。</span></span><br><span class="line"><span class="comment">     **/</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span>[] randomArray(<span class="keyword">int</span> min,<span class="keyword">int</span> max,<span class="keyword">int</span> n)&#123;</span><br><span class="line">        <span class="keyword">int</span> len = max-min+<span class="number">1</span>;<span class="comment">//len为该范围内元素的个数</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(max &lt; min || n &gt; len)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//初始化给定范围的待选数组</span></span><br><span class="line">        <span class="keyword">int</span>[] source = <span class="keyword">new</span> <span class="keyword">int</span>[len];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = min; i &lt; min+len; i++)&#123;</span><br><span class="line">            source[i-min] = i;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span>[] result = <span class="keyword">new</span> <span class="keyword">int</span>[n];</span><br><span class="line">        Random rd = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; result.length; i++) &#123;</span><br><span class="line">            <span class="comment">//待选数组0到(len-2)随机一个下标</span></span><br><span class="line">            index = Math.abs(rd.nextInt() % len--);</span><br><span class="line">            <span class="comment">//将随机到的数放入结果集</span></span><br><span class="line">            result[i] = source[index];</span><br><span class="line">            <span class="comment">//将待选数组中被随机到的数，用待选数组(len-1)下标对应的数替换</span></span><br><span class="line">            source[index] = source[len];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i&lt;=<span class="number">5</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">int</span> result[]=TestRandom.randomArray(<span class="number">2</span>,<span class="number">7</span>,<span class="number">6</span>);</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> num:result) &#123;</span><br><span class="line">                System.out.print(num+<span class="string">"\t"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>6、运行结果如图，可见每次运行的结果都有可能不一样。<br><figure class="image-bubble">                <div class="img-lightbox">                    <div class="overlay"></div>                    <img src="http://images2015.cnblogs.com/blog/927927/201604/927927-20160403222840410-1693410340.png" alt="运行结果" title="">                </div>                <div class="image-caption">运行结果</div>            </figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我们在开发第一个app的时候遇到的一个比较有趣的算法，这种将一个数组重新进行随机排序的问题并不罕见，但是因为是初学者，所以在探讨这个算法的过程中也纠结了很久，当然最后的算法也是参考借鉴了且听风吟博主的一篇文章：&lt;a href=&quot;http://wsjiang.iteye.com/blog/1775341&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://wsjiang.iteye.com/blog/1775341&lt;/a&gt; ，在此鸣谢！&lt;br&gt;
    
    </summary>
    
      <category term="JAVA" scheme="http://www.wensibo.top/categories/JAVA/"/>
    
    
      <category term="数组排列" scheme="http://www.wensibo.top/tags/%E6%95%B0%E7%BB%84%E6%8E%92%E5%88%97/"/>
    
  </entry>
  
</feed>
