<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>BoBoMEe</title>
  <subtitle>好好学习，天天向上</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://bobomee.github.io/"/>
  <updated>2017-05-14T11:17:22.000Z</updated>
  <id>http://bobomee.github.io/</id>
  
  <author>
    <name>BoBoMEe</name>
    <email>wbwjx115@gmail.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>RxJava+Retrofit错误预处理</title>
    <link href="http://bobomee.github.io/2016/08/01/rxjava-retrofit_error/"/>
    <id>http://bobomee.github.io/2016/08/01/rxjava-retrofit_error/</id>
    <published>2016-08-01T03:37:49.000Z</published>
    <updated>2017-05-14T11:17:22.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>在使用 <code>RxJava+Retrofit</code> 结合的网络框架时,为了避免打破流式调用 和 过于繁杂的 <code>Subscribe</code> 代码<br>我们做了很多的尝试,比如 <code>自定义操作符</code>,<code>自定义Transformer</code>,<code>泛型处理</code>,和 自定义 <code>Subscriber</code>等</p>
<h2 id="错误和异常举例"><a href="#错误和异常举例" class="headerlink" title="错误和异常举例"></a>错误和异常举例</h2><p>比如,在服务器返回数据中,假设服务器遵循规范,请求体 一般类似下面这种,</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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">"success"</span>: <span class="keyword">false</span>, <span class="comment">// 是否成功</span></span><br><span class="line">    <span class="string">"code"</span>: <span class="string">"500"</span>,   <span class="comment">// 响应码</span></span><br><span class="line">    <span class="string">"data"</span>: &#123;</span><br><span class="line">        <span class="comment">// 内容,错误的时候返回""</span></span><br><span class="line">    &#125;      </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>这时候,我们我们的 <code>JavaBean</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></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">Response</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">boolean</span> success;</span><br><span class="line">    <span class="keyword">public</span> String code;</span><br><span class="line">    <span class="keyword">public</span> T data;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这种错误 <code>code</code>是我们和服务器约定好的,称为异常,即网络访问成功了.</p>
<p>但是还有一种错误 即: http 错误,比如典型的<br>Retrofit中的 <code>retrofit cache 504 Unsatisfiable Request (only-if-cached) error</code>错误,即网络连接错误.这两种我们都必须处理.</p>
<h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><p>接下来 根据<a href="http://blog.csdn.net/jdsjlzx/article/details/51882661" target="_blank" rel="external">Retrofit+RxJava 优雅的处理服务器返回异常、错误</a>的介绍来进行了一系列的处理,</p>
<p>首先定义如下<code>Transformer</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><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Observable.Transformer&lt;Response&lt;T&gt;, T&gt; sTransformer() &#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> responseObservable -&gt; responseObservable.map(tResponse -&gt; &#123;</span><br><span class="line">     <span class="keyword">if</span> (!tResponse.success) <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(tResponse.code);</span><br><span class="line">     <span class="keyword">return</span> tResponse.data;</span><br><span class="line">   &#125;).onErrorResumeNext(<span class="keyword">new</span> HttpResponseFunc&lt;&gt;());</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> &lt;T&gt; Observable.<span class="function">Transformer&lt;T, T&gt; <span class="title">switchSchedulers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> observable -&gt; observable.subscribeOn(Schedulers.io())</span><br><span class="line">        .observeOn(AndroidSchedulers.mainThread());</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="class"><span class="keyword">class</span> <span class="title">HttpResponseFunc</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Func1</span>&lt;<span class="title">Throwable</span>, <span class="title">Observable</span>&lt;<span class="title">T</span>&gt;&gt; </span>&#123;</span><br><span class="line">   <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Observable&lt;T&gt; <span class="title">call</span><span class="params">(Throwable throwable)</span> </span>&#123;</span><br><span class="line">     <span class="comment">//ExceptionEngine为处理异常的驱动器</span></span><br><span class="line">     <span class="keyword">return</span> Observable.error(<span class="keyword">new</span> Throwable(throwable));</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>在真正调用的地方就可以这样写了</p>
<figure class="highlight java"><table><tr><td class="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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">login</span><span class="params">(View v)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">  apiservice.login(name,pwd)</span><br><span class="line">      .compose(Transformers.sTransformer())</span><br><span class="line">      .compose(Transformers.switchSchedulers())</span><br><span class="line">      .subscribe(subscriber);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Subscriber&lt;UserModel&gt; subscriber = <span class="keyword">new</span> Subscriber&lt;UserModel&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">onCompleted</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="comment">// do onCompleted</span></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">onError</span><span class="params">(Throwable e)</span> </span>&#123;</span><br><span class="line">      <span class="comment">// do on success != true;</span></span><br><span class="line">      <span class="comment">// do on http error</span></span><br><span class="line">      <span class="comment">// do on other error</span></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">onNext</span><span class="params">(UserModel model)</span> </span>&#123;</span><br><span class="line">      <span class="comment">// parse data</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure>
<p>我们的接口是这样的</p>
<figure class="highlight java"><table><tr><td class="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">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Api</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@FormUrlEncoded</span> <span class="meta">@POST</span>(<span class="string">"interface?login"</span>)</span><br><span class="line">Observable&lt;Response&lt;T&gt;&gt; login(<span class="meta">@Field</span>(<span class="string">"name"</span>) String name,<span class="meta">@Field</span>(<span class="string">"pwd"</span>) String pwd);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Transformer-和-Func-处理的区别"><a href="#Transformer-和-Func-处理的区别" class="headerlink" title="Transformer 和 Func 处理的区别"></a>Transformer 和 Func 处理的区别</h2><p>如上的处理,我们定义了 一个 <code>sTransformer</code> 和一个 <code>HttpResponseFunc</code>,<br>从中我们可以明显感觉的到<code>sTransformer</code>其实也是可以用<code>Func1</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></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">login</span><span class="params">(View v)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">  apiservice.login(name,pwd)</span><br><span class="line">      .compose(Transformers.switchSchedulers())</span><br><span class="line">      .map(<span class="keyword">new</span> TransFuc&lt;UserModel&gt;())</span><br><span class="line">      .onErrorReturn(<span class="keyword">new</span> HttpResponseFunc&lt;&gt;())</span><br><span class="line">      .subscribe(subscriber);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TransFuc</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Func1</span>&lt;<span class="title">Response</span>&lt;<span class="title">T</span>&gt;, <span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> T <span class="title">call</span><span class="params">(Response&lt;T&gt; tResponse)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!tResponse.success) <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(tResponse.code);</span><br><span class="line">      <span class="keyword">return</span> tResponse.data;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<p><code>Transformer</code>作用于整个流,<code>Func1</code>是一个操作符,作用于数据项</p>
<h2 id="不规范数据的处理"><a href="#不规范数据的处理" class="headerlink" title="不规范数据的处理"></a>不规范数据的处理</h2><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">&#123;</span><br><span class="line">    &quot;success&quot;: true, // 是否成功</span><br><span class="line">    &quot;status&quot;: &quot;1&quot;,   // 状态码</span><br><span class="line">    &quot;data&quot;: &#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>
<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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;success&quot;: false, // 是否成功</span><br><span class="line">    &quot;status&quot;: &quot;0&quot;,   // 状态码</span><br><span class="line">    &quot;data&quot;: &quot;371&quot;   //错误码  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这时候如果我么用泛型处理</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></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">Response</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">boolean</span> success;</span><br><span class="line">    <span class="keyword">public</span> String status;</span><br><span class="line">    <span class="keyword">public</span> T data;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>针对这种数据,我们的泛型该怎么写成了问题,错误的时候是<code>String</code>,正确的时候是<code>Bean</code>?<br>如果我们直接写成<code>JavaBean</code>,那么我们会得到一个错误,<code>LinkedTreeMap cannot be cast to xxx</code><br>类型转换错误.这时候只能将泛型写成<code>String</code>来处理了,使用如下<code>Transformer</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Observable.<span class="function">Transformer&lt;String, UserModel&gt; <span class="title">trans</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> stringObservable -&gt; stringObservable.map(s -&gt; &#123;</span><br><span class="line"></span><br><span class="line">      Response parseJson = GsonUtil.parseJson(s, Response.class);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span>(<span class="keyword">null</span> != parseJson &amp;&amp; !parseJson.success &amp;&amp; !TextUtils.isEmpty(parseJson.status))&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(parseJson.status);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">null</span> != parseJson &amp;&amp; PatternsUtil.isNum(parseJson.data)) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(parseJson.data);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> GsonUtil.parseJson(s, UserModel.class);</span><br><span class="line">    &#125;).onErrorResumeNext(<span class="keyword">new</span> HttpResponseFunc&lt;&gt;());</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<p>使用就变成了如下这样</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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">login</span><span class="params">(View v)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">  apiservice.login(name,pwd)</span><br><span class="line">      .compose(Transformers.switchSchedulers())</span><br><span class="line">       .compose(Transformers.trans())</span><br><span class="line">      .subscribe(subscriber);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;在使用 &lt;code&gt;RxJava+Retrofit&lt;/code&gt; 结合的网络框架时,为了避免打破流式调用 和 过于繁杂的 &lt;code&gt;Subscribe&lt;/code&gt; 代码&lt;br&gt;我们做了很多的尝试,比如 &lt;code&gt;自定义操作符&lt;/code&gt;,&lt;code&gt;自定义Transformer&lt;/code&gt;,&lt;code&gt;泛型处理&lt;/code&gt;,和 自定义 &lt;code&gt;Subscriber&lt;/code&gt;等&lt;/p&gt;
&lt;h2 id=&quot;错误和异常举例&quot;&gt;&lt;a href=&quot;#错误和异常举例&quot; class=&quot;headerlink&quot; title=&quot;错误和异常举例&quot;&gt;&lt;/a&gt;错误和异常举例&lt;/h2&gt;&lt;p&gt;比如,在服务器返回数据中,假设服务器遵循规范,请求体 一般类似下面这种,&lt;/p&gt;
&lt;figure class=&quot;highlight java&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;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&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;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;string&quot;&gt;&quot;success&quot;&lt;/span&gt;: &lt;span class=&quot;keyword&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;comment&quot;&gt;// 是否成功&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;string&quot;&gt;&quot;code&quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&quot;500&quot;&lt;/span&gt;,   &lt;span class=&quot;comment&quot;&gt;// 响应码&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;string&quot;&gt;&quot;data&quot;&lt;/span&gt;: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// 内容,错误的时候返回&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;      &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="rxjava" scheme="http://bobomee.github.io/tags/rxjava/"/>
    
      <category term="retrofit" scheme="http://bobomee.github.io/tags/retrofit/"/>
    
  </entry>
  
  <entry>
    <title>Rxjava+Retrofit结合开发的封装技巧</title>
    <link href="http://bobomee.github.io/2016/07/31/rx_retrofit/"/>
    <id>http://bobomee.github.io/2016/07/31/rx_retrofit/</id>
    <published>2016-07-30T16:13:46.000Z</published>
    <updated>2017-05-14T11:12:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>在开发中使用<code>RxJava+Retrofit</code>的网络框架,是时下的趋势,使用起来也非常的方便.<br>如果能够在一定程度上进一步封装,能够大大提高我们的开发效率.接下来我们看一下比较常用的简洁处理场景.</p>
<a id="more"></a>
<h2 id="CreateObservable"><a href="#CreateObservable" class="headerlink" title="CreateObservable"></a>CreateObservable</h2><p>我们都知道创建一个<code>Observable</code>可以使用<code>RxJava</code>的创建操作符,比如Create,..等等,<br>在<a href="https://github.com/mgp/effective-rxjava/blob/master/items/convert-functions-to-observable.md" target="_blank" rel="external">effective-rxjava</a>中作者介绍了一种将<code>functions</code>转化为<code>observable</code>的方式,感觉非常新颖,果断使用了.</p>
<p>通过 <code>defer</code> 和 <code>just</code> 操作符方便的将<code>Func0</code>转化为 <code>Observable</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * <span class="doctag">@return</span> an &#123;<span class="doctag">@link</span> Observable&#125; that emits invokes &#123;<span class="doctag">@code</span> function&#125; upon subscription and emits</span><br><span class="line"> *         its value</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;O&gt; <span class="function">Observable&lt;O&gt; <span class="title">makeObservable</span><span class="params">(<span class="keyword">final</span> Func0&lt;O&gt; function)</span> </span>&#123;</span><br><span class="line">    checkNotNull(function);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Observable.defer(() -&gt; Observable.just(function.call()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们都知道<code>defer</code>还有一个好处就是只有订阅时才会生效,而<code>just</code>中的参数采用了泛型化,<br>即我们可以将任意一个有返回值的方法都使用 <code>defer</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Object <span class="title">slowBlockingMethod</span><span class="params">()</span> </span>&#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Observable&lt;Object&gt; <span class="title">newMethod</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> Observable.defer(() -&gt; Observable.just(slowBlockingMethod()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>使用方式</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Observable&lt;Optional&lt;ContentItem&gt;&gt; fetchContentItem(</span><br><span class="line">            <span class="keyword">final</span> ContentItemIdentifier contentItemId) &#123;</span><br><span class="line">        <span class="keyword">return</span> subscribeOnScheduler(() -&gt; mContentDatabase.fetchContentItem(contentItemId));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// more delegating methods follow here ...</span></span><br><span class="line"><span class="keyword">private</span> &lt;T&gt; <span class="function">Observable&lt;T&gt; <span class="title">subscribeOnScheduler</span><span class="params">(<span class="keyword">final</span> Func0&lt;T&gt; function)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> ObservableUtils.makeObservable(function)</span><br><span class="line">                .subscribeOn(mScheduler);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Transformer"><a href="#Transformer" class="headerlink" title="Transformer"></a>Transformer</h2><p>在<a href="http://blog.danlew.net/2015/03/02/dont-break-the-chain/" target="_blank" rel="external">Don’t break the chain: use RxJava’s compose() operator</a>中作者建议使用<code>compose</code>来避免打破<code>RxJava</code>的链式调用,<code>compose</code>中需要传入一个<code>Transformer</code>,我们先来看一下<code>Transformer</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line">  * Transformer function used by &#123;<span class="doctag">@link</span> #compose&#125;.</span><br><span class="line">  * <span class="doctag">@warn</span> more complete description needed</span><br><span class="line">  */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Transformer</span>&lt;<span class="title">T</span>, <span class="title">R</span>&gt; <span class="keyword">extends</span> <span class="title">Func1</span>&lt;<span class="title">Observable</span>&lt;<span class="title">T</span>&gt;, <span class="title">Observable</span>&lt;<span class="title">R</span>&gt;&gt; </span>&#123;</span><br><span class="line">     <span class="comment">// cover for generics insanity</span></span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>从中我们可以看到<code>Transformer</code>是一个<code>Func1</code>,其将一种类型的<code>Observable</code>转换成另一种类型的<code>Observable</code></p>
<h3 id="常见的Transformer用法"><a href="#常见的Transformer用法" class="headerlink" title="常见的Transformer用法"></a>常见的<code>Transformer</code>用法</h3><ul>
<li>线程切换</li>
</ul>
<p>在<code>RxJava</code>中最常用的某过于 <code>线程切换</code> 了,我们定义一个线程切换的<code>RxSchedulerTransformer</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></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">RxSchedulerTransformer</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Observable</span>.<span class="title">Transformer</span>&lt;<span class="title">T</span>, <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">static</span> <span class="keyword">final</span> RxSchedulerTransformer&lt;Object&gt; INSTANCE = <span class="keyword">new</span> RxSchedulerTransformer&lt;&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function">RxSchedulerTransformer&lt;T&gt; <span class="title">instance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (RxSchedulerTransformer&lt;T&gt;) INSTANCE;</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="title">RxSchedulerTransformer</span><span class="params">()</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 class="function"><span class="keyword">public</span> Observable&lt;T&gt; <span class="title">call</span><span class="params">(Observable&lt;T&gt; tObservable)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> tObservable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>错误处理</li>
</ul>
<p>一般和服务器交互取回的<code>json</code>数据结构类似下面这种,当<code>success == true</code>的时候才是正确的返回值,<br><code>success == false</code> 的时候,虽然不会走 <code>RxJava</code>的<code>OnError</code>,但是也是异常.我们希望的是 所有的 异常都在<br><code>OnError</code>中处理.<code>OnNext</code>只关心正确的返回值即可.</p>
<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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    "success": false, // 是否成功</span><br><span class="line">    "code": "500",   // 响应码</span><br><span class="line">    "data": ""      // 内容</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据如上<code>json</code>,我们的<code>Transformers</code>可定义为如下格式,对结果进行一些预处理,只有正常值才返回<code>JavaBean</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><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">RxHandleResultTransformer</span>&lt;<span class="title">T</span>&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title">Observable</span>.<span class="title">Transformer</span>&lt;<span class="title">Transformers</span>.<span class="title">Result</span>&lt;<span class="title">T</span>&gt;, <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">static</span> <span class="keyword">final</span> RxHandleResultTransformer&lt;Object&gt; INSTANCE =</span><br><span class="line">      <span class="keyword">new</span> RxHandleResultTransformer&lt;&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function">RxHandleResultTransformer&lt;T&gt; <span class="title">instance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (RxHandleResultTransformer&lt;T&gt;) INSTANCE;</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="title">RxHandleResultTransformer</span><span class="params">()</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 class="function"><span class="keyword">public</span> Observable&lt;T&gt; <span class="title">call</span><span class="params">(Observable&lt;Transformers.Result&lt;T&gt;&gt; resultObservable)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> resultObservable.flatMap(tResult -&gt; &#123;</span><br><span class="line">      <span class="keyword">if</span> (tResult.success) &#123;</span><br><span class="line">        <span class="keyword">return</span> Observable.just(tResult.data);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Observable.error(<span class="keyword">new</span> RuntimeException(tResult.code));</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">final</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Result</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> success;</span><br><span class="line">    String code;</span><br><span class="line">    T data;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="自定义操作符"><a href="#自定义操作符" class="headerlink" title="自定义操作符"></a>自定义操作符</h2><p>和<code>compose</code>不同的是,自定义<code>Operator</code> 作用于Observable发射的单独的数据项,<code>compose</code>作用于整个流, 自定义操作符是和<code>lift</code>一起使用的,自定义操作符需要实现<code>Operator</code></p>
<p>更多关于自定义操作符的介绍;<br><a href="https://mcxiaoke.gitbooks.io/rxdocs/content/topics/Implementing-Your-Own-Operators.html" target="_blank" rel="external">实现自己的操作符</a><br><a href="http://mushuichuan.com/2016/02/05/rxjava-operator-10/#more" target="_blank" rel="external">RxJava操作符（十）自定义操作符</a></p>
<h2 id="三级缓存"><a href="#三级缓存" class="headerlink" title="三级缓存"></a>三级缓存</h2><p>其实<code>Retrofit</code>是可以处理缓存的,相关介绍:<a href="http://blog.csdn.net/wbwjx/article/details/51379506" target="_blank" rel="external">Retrofit2.0使用总结及注意事项</a>,<br>这里需要注意的是<font color="red">Retrofit缓存需要使用@GET才生效</font>,而且是使用的文件存储.</p>
<p>关于<code>RxJava</code>缓存参考:<a href="http://blog.csdn.net/lzyzsd/article/details/50120801" target="_blank" rel="external">RxJava使用场景小结</a>,用<code>RxJava</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><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">Observable&lt;String&gt; memory = Observable.create(<span class="keyword">new</span> Observable.OnSubscribe&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">call</span><span class="params">(Subscriber&lt;? <span class="keyword">super</span> String&gt; subscriber)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (memoryCache != <span class="keyword">null</span>) &#123;</span><br><span class="line">            subscriber.onNext(memoryCache);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            subscriber.onCompleted();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line">Observable&lt;String&gt; disk = Observable.create(<span class="keyword">new</span> Observable.OnSubscribe&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">call</span><span class="params">(Subscriber&lt;? <span class="keyword">super</span> String&gt; subscriber)</span> </span>&#123;</span><br><span class="line">      String cachePref = rxPreferences.getString(<span class="string">"cache"</span>).get();</span><br><span class="line">      <span class="keyword">if</span> (!TextUtils.isEmpty(cachePref)) &#123;</span><br><span class="line">        subscriber.onNext(cachePref);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        subscriber.onCompleted();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;).onErrorReturn(throwable-&gt;<span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line">Observable&lt;String&gt; network = Observable.just(<span class="string">"network"</span>).onErrorReturn(throwable -&gt; <span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//主要就是靠concat operator来实现</span></span><br><span class="line">Observable.concat(memory, disk, network)</span><br><span class="line">.first().subscriber(observer);</span><br></pre></td></tr></table></figure>
<p>这里用到了<code>concat</code>与<code>onErrorReturn</code>两个操作符,关于<code>concat(连接操作符)</code>,<a href="http://reactivex.io/documentation/operators/concat.html" target="_blank" rel="external">官方解释</a></p>
<p>关于<code>onErrorReturn(错误处理操作符)</code>,可以看这里:<a href="http://blog.csdn.net/wbwjx/article/details/51292219" target="_blank" rel="external">RxJava错误处理</a></p>
<h2 id="屏幕切换"><a href="#屏幕切换" class="headerlink" title="屏幕切换"></a>屏幕切换</h2><p>存在如下两种问题:</p>
<ol>
<li><p>我们知道 屏幕切换等配置发生改变的时候,会导致<code>Activity</code>的重建,当我们订阅了某一个<code>Subscribtion</code>后,屏幕发生了改变,<br>及调用了<code>unsubscribe</code>方法,如何才能保证<code>Subscribtion</code>的延续呢?</p>
</li>
<li><p>在<code>Android</code>中<code>Context</code>是导致很多 内存泄漏的罪魁祸首.如果我们创建的<code>subscribtion</code>持有了<code>Context</code>将会变得十分的危险,<br>如果<code>Observable</code>没有准时完成,就很容易导致内存泄漏.</p>
</li>
</ol>
<p>第一种问题,可以用<code>RxJava</code>的缓存机制解决,就是<code>cache</code>(或是<code>replay()</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></pre></td><td class="code"><pre><span class="line">Observable&lt;Photo&gt; request = service.getUserPhoto(id).cache();</span><br><span class="line">Subscription sub = request.subscribe(photo -&gt; handleUserPhoto(photo));</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...When the Activity is being recreated...</span></span><br><span class="line">sub.unsubscribe();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...Once the Activity is recreated...</span></span><br><span class="line">request.subscribe(photo -&gt; handleUserPhoto(photo));</span><br></pre></td></tr></table></figure>
<p>第二个问题的解决方案就是在生命周期的某个时刻取消订阅。采用<code>CompositeSubscription</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><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"> CompositeSubscription mCompositeSubscription;</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">addSubscription</span><span class="params">(Subscription s)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">this</span>.mCompositeSubscription == <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.mCompositeSubscription = <span class="keyword">new</span> CompositeSubscription();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">null</span> != s)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">this</span>.mCompositeSubscription.add(s);</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">unsubscribe</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">this</span>.mCompositeSubscription != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.mCompositeSubscription.clear();</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">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">   unsubscribe();</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<h2 id="RxAndroid-中好用的方法"><a href="#RxAndroid-中好用的方法" class="headerlink" title="RxAndroid 中好用的方法"></a>RxAndroid 中好用的方法</h2><p><code>RxAndroid</code>为我们提供了很多好用的API,如<code>HandlerThreadScheduler</code>,<code>AndroidObservable</code>,<code>ViewObservable</code><br>其中<code>HandlerThreadScheduler</code>是一个可以绑定到<code>Handler</code>上的<code>scheduler</code>,<br><code>AndroidObservable</code>可以绑定到 <code>Activity</code> 或 <code>Fragment</code>,方便生命周期的管理.同时可以方便的创建一个<code>BroadCastReceiver</code>的<code>Observable</code>,<br><code>ViewObservable</code>用于给<code>View</code>添加绑定,如 <code>ViewObservable.clicks()</code>(监听<code>View</code>的点击事件)或者 <code>ViewObservable.text()</code>(监听<code>TextView</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></pre></td><td class="code"><pre><span class="line">AndroidObservable.bindActivity(<span class="keyword">this</span>, retrofitService.getImage(url))</span><br><span class="line">    .subscribeOn(Schedulers.io())</span><br><span class="line">    .subscribe(bitmap -&gt; myImageView.setImageBitmap(bitmap));</span><br><span class="line"></span><br><span class="line">IntentFilter filter = <span class="keyword">new</span> IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);</span><br><span class="line">AndroidObservable.fromBroadcast(context, filter)</span><br><span class="line">    .subscribe(intent -&gt; handleConnectivityChange(intent));</span><br><span class="line"></span><br><span class="line">ViewObservable.clicks(mCardNameEditText, <span class="keyword">false</span>)</span><br><span class="line">    .subscribe(view -&gt; handleClick(view));</span><br></pre></td></tr></table></figure>
<p>参考:<a href="http://blog.csdn.net/lzyzsd/article/details/50120801" target="_blank" rel="external">RxJava使用场景小结</a><br><a href="http://imxie.cc/2016/05/24/RxJava-Retrofit-%E7%9A%84%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/?utm_source=tuicool&amp;utm_medium=referral" target="_blank" rel="external">RxJava + Retrofit 的实际应用场景</a><br><a href="http://blog.danlew.net/2015/03/02/dont-break-the-chain/" target="_blank" rel="external">Don’t break the chain: use RxJava’s compose() operator</a><br><a href="https://github.com/mgp/effective-rxjava/blob/master/items/convert-functions-to-observable.md" target="_blank" rel="external">effective-rxjava</a><br><a href="https://mcxiaoke.gitbooks.io/rxdocs/content/topics/Implementing-Your-Own-Operators.html" target="_blank" rel="external">实现自己的操作符</a><br><a href="http://mushuichuan.com/2016/02/05/rxjava-operator-10/#more" target="_blank" rel="external">RxJava操作符（十）自定义操作符</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;在开发中使用&lt;code&gt;RxJava+Retrofit&lt;/code&gt;的网络框架,是时下的趋势,使用起来也非常的方便.&lt;br&gt;如果能够在一定程度上进一步封装,能够大大提高我们的开发效率.接下来我们看一下比较常用的简洁处理场景.&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="rxjava" scheme="http://bobomee.github.io/tags/rxjava/"/>
    
      <category term="retrofit" scheme="http://bobomee.github.io/tags/retrofit/"/>
    
  </entry>
  
  <entry>
    <title>EditText 背景,光标着色及其原理解析</title>
    <link href="http://bobomee.github.io/2016/07/27/drawablecompat/"/>
    <id>http://bobomee.github.io/2016/07/27/drawablecompat/</id>
    <published>2016-07-27T03:48:32.000Z</published>
    <updated>2017-05-14T11:06:15.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>看到 <a href="http://androidweekly.net/" target="_blank" rel="external">Android Weekly</a> 最新一期有一篇文章：<a href="http://andraskindler.com/blog/2015/tinting_drawables/" target="_blank" rel="external">Tinting drawables</a>，使用 <code>ColorFilter</code> 手动打造了一个 <code>TintBitmapDrawable</code>，之前也看到有些文章使用这种方式来实现 <code>Drawable</code> 着色或者实现类似的功能。但是，这种方案并不完善，本文将介绍一个完美的后向兼容方案。</p>
<a id="more"></a>
<h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>其实在 <code>Android Support V4</code> 的包中提供了 <code>DrawableCompat</code> 类，我们很容易写出如下的辅助方法来实现 <code>Drawable</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Drawable <span class="title">tintDrawable</span><span class="params">(Drawable drawable, ColorStateList colors)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Drawable wrappedDrawable = DrawableCompat.wrap(drawable);</span><br><span class="line">    DrawableCompat.setTintList(wrappedDrawable, colors);</span><br><span class="line">    <span class="keyword">return</span> wrappedDrawable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>使用例子：</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">EditText editText1 = (EditText) findViewById(R.id.edit_1);</span><br><span class="line"><span class="keyword">final</span> Drawable originalDrawable = editText1.getBackground();</span><br><span class="line"><span class="keyword">final</span> Drawable wrappedDrawable = tintDrawable(originalDrawable, ColorStateList.valueOf(Color.RED));</span><br><span class="line">editText1.setBackgroundDrawable(wrappedDrawable);</span><br><span class="line"></span><br><span class="line">EditText editText2 = (EditText) findViewById(R.id.edit_2);</span><br><span class="line">editText2.setBackgroundDrawable(tintDrawable(editText2.getBackground(),</span><br><span class="line">ColorStateList.valueOf(Color.parseColor(<span class="string">"#03A9F4"</span>))));</span><br></pre></td></tr></table></figure>
<p>效果如下：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/tint-edittext.jpg" alt="tint着色效果"></p>
<p>对比 <code>Tinting drawables</code> 文中的方法，除了它拥有的优势以外，这种方式支持几乎所有的 <code>Drawable</code> 类型，并且能够完美兼容几乎所有的 <code>Android</code> 版本。</p>
<p>到这里，其实本文要说的解决方案已经说完了。如果继续往下看，相信会有更多收获。</p>
<h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><h3 id="使用-ColorStateList-着色"><a href="#使用-ColorStateList-着色" class="headerlink" title="使用 ColorStateList 着色"></a>使用 <code>ColorStateList</code> 着色</h3><p>这种方式支持使用 <code>ColorStateList</code> 着色，这样我们还可以根据 <code>View</code> 的状态着色成不同的颜色。 对于上面的 <code>EditText</code> 的例子，我们就可以优化一下，根据它是否获得焦点，设置成不同的颜色。我们新建一个 <code>res/color/edittext_tint_colors.xml</code> 如下：</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">selector</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span> <span class="attr">android:color</span>=<span class="string">"@color/red"</span> <span class="attr">android:state_focused</span>=<span class="string">"true"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span> <span class="attr">android:color</span>=<span class="string">"@color/gray"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">selector</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>代码改成这样：</p>
<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">editText2.setBackgroundDrawable(tintDrawable(editText2.getBackground(),</span><br><span class="line">getResources().getColorStateList(R.color.edittext_tint_colors)));</span><br></pre></td></tr></table></figure>
<h3 id="BitmapDrawable-的优化"><a href="#BitmapDrawable-的优化" class="headerlink" title="BitmapDrawable 的优化"></a><code>BitmapDrawable</code> 的优化</h3><p>首先来看一下问题。原始的 <code>Icon</code> 如下图所示：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/ic_account_balance_black_24dp.png" alt="icon"></p>
<p>我们使用两个 <code>ImageView</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></pre></td><td class="code"><pre><span class="line">ImageView imageView = (ImageView) findViewById(R.id.image_1);</span><br><span class="line"><span class="keyword">final</span> Drawable originalBitmapDrawable = getResources().getDrawable(R.drawable.icon);</span><br><span class="line">imageView.setImageDrawable(tintDrawable(originalBitmapDrawable, ColorStateList.valueOf(Color.MAGENTA)));</span><br></pre></td></tr></table></figure>
<p>效果如下：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/tint-bitmap-error.png" alt="ColorStateList着色"></p>
<p>怎么回事？我明明只给后面的一个设置了着色的 <code>Drawable</code>，为什么两个都被着色了？这是因为 <code>Android</code> 为了优化系统性能，资源 <code>Drawable</code> 只有一份拷贝，你修改了它，等于所有的都修改了。如果你给两个 <code>View</code> 设置同一个资源，它的状态是这样的：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/shared_states.png" alt="Drawable状态"></p>
<p>也是就是他们是共享状态的。幸运的是，<code>Drawable</code> 提供了一个方法 <code>mutate()</code>，来打破这种共享状态，等于就是要告诉系统，我要修改<code>（mutate）</code>这个 <code>Drawable</code>。给 <code>Drawable</code> 调用 <code>mutate()</code> 方法以后。他们的关系就变成如下的图所示：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/mutated_states.png" alt="mutate()状态"></p>
<p>我们修改一下代码：</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">ImageView imageView = (ImageView) findViewById(R.id.image_1);</span><br><span class="line"><span class="keyword">final</span> Drawable originalBitmapDrawable = getResources().getDrawable(R.drawable.icon).mutate();</span><br><span class="line">imageView.setImageDrawable(tintDrawable(originalBitmapDrawable, ColorStateList.valueOf(Color.MAGENTA)));</span><br></pre></td></tr></table></figure>
<p>得到的效果如下：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/tint-bitmap-mutate.png" alt="ColorStateList状态"></p>
<p>非常完美，达到了我们之前想要的效果。</p>
<p>你可能会有这样的担心，调用 <code>mutate()</code> 是不是在内存中把 <code>Bitmap</code> 拷贝了一份？其实不是这样的，还是公用的 <code>Bitmap</code>，只是拷贝了一份状态值，这个数据量很小，所以不用担心。详细情况可以参考这篇文章：<a href="http://android-developers.blogspot.hk/2009/05/drawable-mutations.html" target="_blank" rel="external">Drawable mutations</a>。</p>
<h2 id="EditText-光标着色"><a href="#EditText-光标着色" class="headerlink" title="EditText 光标着色"></a><code>EditText</code> 光标着色</h2><p>通过前面的方法，我们已经可以把 <code>EditText</code> 的背景着色（<code>Tint）</code>成了任意想要的颜色。但是仔细一看，还有点问题，输入的时候，光标的颜色还是原来的颜色，如下图所示：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/tint-edittext-1.jpg" alt="EditText 光标着色"></p>
<p>在 <code>Android 3.1 (API 12)</code> 开始就支持了 <code>textCursorDrawable</code>，也就是可以自定义光标的 <code>Drawable</code>。遗憾的是，这个方法只能在 <code>xml</code> 中使用，这和本文没有啥关系，具体使用可以参考<a href="http://stackoverflow.com/questions/7238450/set-edittext-cursor-color" target="_blank" rel="external">这个回答@stackoverflow</a>，并没有提供接口来动态修改。</p>
<p>我们有一个比较折中的方案，就是通过反射机制，来获得 <code>CursorDrawable</code>，然后通过本文的方法，来对这个 <code>Drawable</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><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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">tintCursorDrawable</span><span class="params">(EditText editText, <span class="keyword">int</span> color)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Field fCursorDrawableRes = TextView.class.getDeclaredField(<span class="string">"mCursorDrawableRes"</span>);</span><br><span class="line">        fCursorDrawableRes.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">        <span class="keyword">int</span> mCursorDrawableRes = fCursorDrawableRes.getInt(editText);</span><br><span class="line">        Field fEditor = TextView.class.getDeclaredField(<span class="string">"mEditor"</span>);</span><br><span class="line">        fEditor.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">        Object editor = fEditor.get(editText);</span><br><span class="line">        Class&lt;?&gt; clazz = editor.getClass();</span><br><span class="line">        Field fCursorDrawable = clazz.getDeclaredField(<span class="string">"mCursorDrawable"</span>);</span><br><span class="line">        fCursorDrawable.setAccessible(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mCursorDrawableRes &lt;= <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">        Drawable cursorDrawable = editText.getContext().getResources().getDrawable(mCursorDrawableRes);</span><br><span class="line">        <span class="keyword">if</span> (cursorDrawable == <span class="keyword">null</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">        Drawable tintDrawable  = tintDrawable(cursorDrawable, ColorStateList.valueOf(color));</span><br><span class="line">        Drawable[] drawables = <span class="keyword">new</span> Drawable[] &#123;tintDrawable, tintDrawable&#125;;</span><br><span class="line">        fCursorDrawable.set(editor, drawables);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable ignored) &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>原理比较简单，就是直接获得到 <code>EditText</code> 的 <code>mCursorDrawableRes</code>，然后通过这个 <code>id</code> 获取到对应的 <code>Drawable</code>，调用我们的着色函数 <code>tintDrawable</code>，然后设置进去。效果如下：</p>
<p><img src="http://7rf9ir.com1.z0.glb.clouddn.com/tint-edittext-2.png" alt="EditText光标着色"></p>
<h2 id="原理分析"><a href="#原理分析" class="headerlink" title="原理分析"></a>原理分析</h2><p>上面就是我们的全部的解决方案，我们接下来分析一下 <code>DrawableCompat</code> 着色相关的源码，理解其中的原理。再来回顾一下我们写的 <code>tintDrawable</code> 函数，里面只调用了 <code>DrawableCompat</code> 的两个方法。下面我们详细分析这两个方法。</p>
<p>首先通过 <code>DrawableCompat.wrap()</code> 获得一个封装的 <code>Drawable</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// android.support.v4.graphics.drawable.DrawableCompat.java</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Drawable <span class="title">wrap</span><span class="params">(Drawable drawable)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> IMPL.wrap(drawable);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>调用了 <code>IMPL</code> 的 <code>wrap</code> 函数，<code>IMPL</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><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="comment">/**</span><br><span class="line">* Select the correct implementation to use for the current platform.</span><br><span class="line">*/</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> DrawableImpl IMPL;</span><br><span class="line"><span class="keyword">static</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> version = android.os.Build.VERSION.SDK_INT;</span><br><span class="line">    <span class="keyword">if</span> (version &gt;= <span class="number">23</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> MDrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (version &gt;= <span class="number">22</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> LollipopMr1DrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (version &gt;= <span class="number">21</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> LollipopDrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (version &gt;= <span class="number">19</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> KitKatDrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (version &gt;= <span class="number">17</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> JellybeanMr1DrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (version &gt;= <span class="number">11</span>) &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> HoneycombDrawableImpl();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        IMPL = <span class="keyword">new</span> BaseDrawableImpl();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>很明显，这是根据不同的 <code>API Level</code> 选择不同的实现类，再往下看一点，发现 <code>API Level</code> 大于等于 <code>22</code> 的继承于 <code>LollipopMr1DrawableImpl</code>，我们来看一下它的 <code>wrap()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">LollipopMr1DrawableImpl</span> <span class="keyword">extends</span> <span class="title">LollipopDrawableImpl</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> Drawable <span class="title">wrap</span><span class="params">(Drawable drawable)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DrawableCompatApi22.wrapForTinting(drawable);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DrawableCompatApi22</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> Drawable <span class="title">wrapForTinting</span><span class="params">(Drawable drawable)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// We don't need to wrap anything in Lollipop-MR1</span></span><br><span class="line">        <span class="keyword">return</span> drawable;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>因为 <code>API 22</code>开始 <code>Drwable</code> 本来就支持了 <code>Tint</code>，不需要做任何封装了。 我们来看一下它的 <code>wrap()</code> 都是返回一个封装了一层的 <code>Drawable</code>，我们以 <code>BaseDrawableImpl</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseDrawableImpl</span> <span class="keyword">implements</span> <span class="title">DrawableImpl</span> </span>&#123;</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> Drawable <span class="title">wrap</span><span class="params">(Drawable drawable)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DrawableCompatBase.wrapForTinting(drawable);</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>这里调用了 <code>DrawableCompatBase.wrapForTinting()</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DrawableCompatBase</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> Drawable <span class="title">wrapForTinting</span><span class="params">(Drawable drawable)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!(drawable <span class="keyword">instanceof</span> DrawableWrapperDonut)) &#123;</span><br><span class="line">          <span class="keyword">return</span> <span class="keyword">new</span> DrawableWrapperDonut(drawable);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> drawable;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>实际上这里是返回了一个 <code>DrawableWrapperDonut</code> 的封装对象。同理分析其他 <code>API Level</code> 小于 <code>22</code> 的最后实现，发现最后都是返回一个继承于 <code>DrawableWrapperDonut</code> 的对象。</p>
<p>回到最开始的代码，我们分析 <code>DrawableCompat.setTintList()</code> 的实现，其实是调用了 <code>IMPL.setTintList()</code>，通过前面的分析我们知道，只有 <code>API Level</code> 小于 <code>22</code> 的才要做特殊的处理，我们还是以 <code>BaseDrawableImpl</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseDrawableImpl</span> <span class="keyword">implements</span> <span class="title">DrawableImpl</span> </span>&#123;</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">setTintList</span><span class="params">(Drawable drawable, ColorStateList tint)</span> </span>&#123;</span><br><span class="line">        DrawableCompatBase.setTintList(drawable, tint);</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>这里调用了 <code>DrawableCompatBase.setTintList()</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DrawableCompatBase</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> <span class="keyword">void</span> <span class="title">setTintList</span><span class="params">(Drawable drawable, ColorStateList tint)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (drawable <span class="keyword">instanceof</span> DrawableWrapper) &#123;</span><br><span class="line">            ((DrawableWrapper) drawable).setTintList(tint);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过前面的分析，我们知道，这里传入的 <code>Drawable</code> 都是 <code>DrawableWrapperDonut</code> 的子类，所以实际上就是调用了 <code>DrawableWrapperDonut</code> 的 <code>setTintList()</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><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="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTintList</span><span class="params">(ColorStateList tint)</span> </span>&#123;</span><br><span class="line">    mTintList = tint;</span><br><span class="line">    updateTint(getState());</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">boolean</span> <span class="title">updateTint</span><span class="params">(<span class="keyword">int</span>[] state)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mTintList != <span class="keyword">null</span> &amp;&amp; mTintMode != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> color = mTintList.getColorForState(state, mTintList.getDefaultColor());</span><br><span class="line">        <span class="keyword">final</span> PorterDuff.Mode mode = mTintMode;</span><br><span class="line">        <span class="keyword">if</span> (!mColorFilterSet || color != mCurrentColor || mode != mCurrentMode) &#123;</span><br><span class="line">            setColorFilter(color, mode);</span><br><span class="line">            mCurrentColor = color;</span><br><span class="line">            mCurrentMode = mode;</span><br><span class="line">            mColorFilterSet = <span class="keyword">true</span>;</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 class="keyword">else</span> &#123;</span><br><span class="line">        mColorFilterSet = <span class="keyword">false</span>;</span><br><span class="line">        clearColorFilter();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>看到这里最终是调用了 <code>Drawable</code> 的 <code>setColorFilter()</code> 方法。可以看到，这里和最开始提到的那篇文章的原理是一致的，但是这里处理更加细致，考虑更加全面。</p>
<p>通过源码分析，感觉到可能这才是<em>做 <code>Android</code> 后向兼容库的正确姿势</em>吧。</p>
<p>原文出自:<a href="http://www.race604.com/tint-drawable/" target="_blank" rel="external">Drawable 着色的后向兼容方案</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;看到 &lt;a href=&quot;http://androidweekly.net/&quot;&gt;Android Weekly&lt;/a&gt; 最新一期有一篇文章：&lt;a href=&quot;http://andraskindler.com/blog/2015/tinting_drawables/&quot;&gt;Tinting drawables&lt;/a&gt;，使用 &lt;code&gt;ColorFilter&lt;/code&gt; 手动打造了一个 &lt;code&gt;TintBitmapDrawable&lt;/code&gt;，之前也看到有些文章使用这种方式来实现 &lt;code&gt;Drawable&lt;/code&gt; 着色或者实现类似的功能。但是，这种方案并不完善，本文将介绍一个完美的后向兼容方案。&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="EditText" scheme="http://bobomee.github.io/tags/EditText/"/>
    
  </entry>
  
  <entry>
    <title>Android RecyclerView 使用全解析</title>
    <link href="http://bobomee.github.io/2016/06/26/recyclerview/"/>
    <id>http://bobomee.github.io/2016/06/26/recyclerview/</id>
    <published>2016-06-26T06:13:36.000Z</published>
    <updated>2017-05-14T11:11:07.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p><code>RecyclerView</code>出来已经很长时间了,关于其的介绍也非常的多.作为<code>ListView</code>的升级版,它更加强大和灵活.<br>可以轻松的实现各种布局和动画,见其名,知其意.<code>RecyclerView</code>用于在有限窗口中展示大量数据集合的可复用的视图.<br>这里主要梳理一下<code>Recyclerview</code>的常用方法,示例Demo:<a href="https://github.com/BoBoMEe/AndroidDev/tree/blog/blogcodes/app/src/main/java/com/bobomee/blogdemos/recycler" target="_blank" rel="external">BoBoMEe/AndroidDev</a></p>
<a id="more"></a>
<h2 id="相关类"><a href="#相关类" class="headerlink" title="相关类"></a>相关类</h2><p><code>RecyclerView</code>的灵活性,主要体现在各个类的职责分离上,<br><code>RecyclerView</code>不负责子View的展示与布局,只负责子view的回收与复用.其他的比如,子View的布局,装饰及动画都交由其他类来处理.</p>
<p><code>RecyclerView</code>相关类及其作用</p>
<table>
<thead>
<tr>
<th>类</th>
<th style="text-align:center">用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>RecyclerView.ViewHolder</td>
<td style="text-align:center">装载子view数据的容器</td>
</tr>
<tr>
<td>RecyclerView.LayoutManager</td>
<td style="text-align:center">布局管理器,负责子View的布局</td>
</tr>
<tr>
<td>RecyclerView.Adapter</td>
<td style="text-align:center">适配器,负责处理数据与绑定视图</td>
</tr>
<tr>
<td>RecyclerView.ItemDecoration</td>
<td style="text-align:center">装饰子view,如分割线、偏移等</td>
</tr>
<tr>
<td>RecyclerView.ItemAnimator</td>
<td style="text-align:center">子view改变时的动画</td>
</tr>
</tbody>
</table>
<h2 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h2><p><code>RecyclerView</code>最简单的使用需要设置<code>LayoutManager</code>和<code>Adapter</code>,其他的都是可选项.简单使用流程可分为以下几步.</p>
<ul>
<li>引入</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">compile <span class="string">'com.android.support:recyclerview-v7:23.4.0'</span></span><br></pre></td></tr></table></figure>
<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">android.support.v7.widget.RecyclerView</span></span><br><span class="line">    <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    <span class="attr">android:id</span>=<span class="string">"@+id/recycler_view"</span></span><br><span class="line">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span><br><span class="line">    <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span><br><span class="line">    <span class="attr">android:padding</span>=<span class="string">"@dimen/item_margin"</span></span><br><span class="line">    <span class="attr">android:clipToPadding</span>=<span class="string">"false"</span>/&gt;</span></span><br></pre></td></tr></table></figure>
<ul>
<li>设置<code>Adapter</code>和<code>LayoutManager</code></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></pre></td><td class="code"><pre><span class="line">RecyclerView mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);</span><br><span class="line"></span><br><span class="line">mLinearLayoutManager = <span class="keyword">new</span> LinearLayoutManager(<span class="keyword">this</span>);</span><br><span class="line">mRecyclerView.setLayoutManager(mLinearLayoutManager);</span><br><span class="line"></span><br><span class="line">mRecyclerStringAdapter = <span class="keyword">new</span> RecyclerStringAdapter(Datas.getDatas());</span><br><span class="line">mRecyclerView.setAdapter(mRecyclerStringAdapter);</span><br></pre></td></tr></table></figure>
<p>其中<code>LinearLayoutManager</code>代表线性显示.默认是方向为<code>VERTICAL</code>,<code>LayoutManager</code>抽象类有三个官方实现版本,分别是<code>LinearLayoutManager</code>(线性布局),<code>GridLayoutManager</code>(网格布局),<code>StaggeredGridLayoutManager</code>(瀑布流布局).</p>
<ul>
<li><code>Adapter</code></li>
</ul>
<p>不同于<code>ListView</code>的<code>Adapter</code>,<code>Recyclerview</code>的适配器强制使用<code>ViewHolder</code>模式.如下一个简单的<code>Adapter</code>实现</p>
<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><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">public class RecyclerStringAdapter extends RecyclerView.Adapter&lt;RecyclerViewholder&gt; &#123;</span><br><span class="line"></span><br><span class="line">  List&lt;String&gt; datas;</span><br><span class="line">  public RecyclerStringAdapter(List&lt;String&gt; datas) &#123;</span><br><span class="line">    this.datas = datas;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  @Override public void onBindViewHolder(RecyclerViewholder holder, int position) &#123;</span><br><span class="line">    holder.textView.setText(datas.get(position));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  @Override public RecyclerViewholder onCreateViewHolder(ViewGroup parent, int viewType) &#123;</span><br><span class="line"></span><br><span class="line">   View view =</span><br><span class="line">       LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);</span><br><span class="line"></span><br><span class="line">   return new RecyclerViewholder(view);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> @Override public int getItemCount() &#123;</span><br><span class="line">    return datas.size();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"># RecyclerViewholder</span><br><span class="line">public class RecyclerViewholder extends RecyclerView.ViewHolder &#123;</span><br><span class="line"></span><br><span class="line">  public TextView textView;</span><br><span class="line"></span><br><span class="line">  public RecyclerViewholder(View itemView) &#123;</span><br><span class="line">    super(itemView);</span><br><span class="line">    textView = (TextView) itemView.findViewById(R.id.text);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="其他用法"><a href="#其他用法" class="headerlink" title="其他用法"></a>其他用法</h2><ul>
<li><code>LayoutManager</code>布局切换</li>
</ul>
<p>我们知道<code>LayoutManager</code>主要负责子View的布局.通过设置不同的<code>LayoutManager</code>即可实现展现方式的改变.<br>如果不设置<code>LayoutManager</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//gridview的列数为3,orientation = VERTICAL</span></span><br><span class="line">mGridLayoutManager = <span class="keyword">new</span> GridLayoutManager(<span class="keyword">this</span>, <span class="number">3</span>);</span><br><span class="line">mRecyclerView.setLayoutManager(mGridLayoutManager);</span><br></pre></td></tr></table></figure>
<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">// 瀑布流的列数为3,orientation = VERTICAL</span></span><br><span class="line">mStaggeredGridLayoutManager =</span><br><span class="line">        <span class="keyword">new</span> StaggeredGridLayoutManager(<span class="number">3</span>, StaggeredGridLayoutManager.VERTICAL);</span><br><span class="line">mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);</span><br></pre></td></tr></table></figure>
<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="keyword">int</span> orientation = mLinearLayoutManager.getOrientation();</span><br><span class="line">mLinearLayoutManager.setOrientation(orientation == LinearLayoutManager.VERTICAL ? LinearLayoutManager.HORIZONTAL: LinearLayoutManager.VERTICAL);</span><br></pre></td></tr></table></figure>
<ul>
<li><code>LayoutManager</code>常用方法</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//返回当前第一个可见 Item 的 position</span></span><br><span class="line">findFirstVisibleItemPosition()</span><br><span class="line"><span class="comment">//返回当前第一个完全可见 Item 的 position</span></span><br><span class="line">findFirstCompletelyVisibleItemPosition()</span><br><span class="line"><span class="comment">//返回当前最后一个可见 Item 的 position</span></span><br><span class="line">findLastVisibleItemPosition()</span><br><span class="line"><span class="comment">//返回当前最后一个完全可见 Item 的 position.</span></span><br><span class="line">findLastCompletelyVisibleItemPosition()</span><br></pre></td></tr></table></figure>
<ul>
<li><code>ItemAnimator</code>Item动画</li>
</ul>
<p>用于设置子view的添加、移动、删除的相关动画效果.官方默认提供了一个<code>DefaultItemAnimator</code>,如果我们没有设置<code>ItemAnimator</code>,则默认就是<code>DefaultItemAnimator</code>,<code>Recyclerview.Adapter</code>提供了<code>notifyDataSetChanged()</code>、<code>notifyItemInserted(index)</code>、 <code>notifyItemRemoved(position)</code>、 <code>notifyItemChanged(position)</code>方法来刷新<code>Recyclerview</code>.这里可以看一下<a href="https://github.com/wasabeef/recyclerview-animators" target="_blank" rel="external">wasabeef/recyclerview-animators</a>:实现了多种<code>Recyclerview</code>的Item动画</p>
<ul>
<li><code>ItemDecoration</code>Item装饰</li>
</ul>
<p>通过<code>mRecyclerView.addItemDecoration(new MarginDecoration(this));</code>来给Item添加装饰,</p>
<p><code>ItemDecoration</code>可以叠加,<code>Recyclerview</code>展示的时候会遍历所有的<code>ItemDecoration</code>并调用其绘制方法来对Item进行装饰.</p>
<p><code>ItemDecoration</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Item条目绘制之前调用,如果不是指offset则被Item的内容所遮挡</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas c, RecyclerView parent)</span></span>;</span><br><span class="line"><span class="comment">// 在Item条目绘制之后调用,浮于Item之上</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDrawOver</span><span class="params">(Canvas c, RecyclerView parent)</span></span>;</span><br><span class="line"><span class="comment">// 计算并设置Item偏移量</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getItemOffsets</span><span class="params">(Rect outRect, <span class="keyword">int</span> itemPosition, RecyclerView parent)</span></span>;</span><br></pre></td></tr></table></figure>
<h2 id="Recyclerview多种布局"><a href="#Recyclerview多种布局" class="headerlink" title="Recyclerview多种布局"></a>Recyclerview多种布局</h2><p>在<code>ListView</code>中为了实现多种布局,我们需要重写<code>Adapter</code>中的 <code>getItemViewType</code>和<code>getViewTypeCount</code>方法<br>而在<code>Recyclerview</code>中保留了<code>getItemViewType</code>方法,同时我们可以在<code>onCreateViewHolder</code>中根据不同的<code>ViewType</code>来创建不同的<code>Holder</code><br>这里可以看一下<a href="https://github.com/hongyangAndroid/baseAdapter" target="_blank" rel="external">hongyangAndroid/baseAdapter</a>:万能的Adapter for ListView,RecyclerView,GridView<br>其中关于<code>HeaderView</code>及<code>FooterwView</code>的处理,同样也是通过<code>ViewType</code>来实现的,<br>在网格布局中,依靠了<code>GridLayoutManager#setSpanSizeLookup</code>方法来设置每个Item占多少个span,<br>在瀑布流不居中,通过给<code>StaggeredGridLayoutManager.LayoutParams</code>设置<code>setFullSpan(true);</code>来占满一行.</p>
<h2 id="ItemTouchHelper"><a href="#ItemTouchHelper" class="headerlink" title="ItemTouchHelper"></a>ItemTouchHelper</h2><p>用于<code>Recyclerview</code>的触摸辅助操作,比如选中,移动,删除等事件的监听.<br>这里可以看一下: <a href="http://www.devtf.cn/?p=795" target="_blank" rel="external">可拖拽的RecyclerView</a>,通过跟踪代码我们可以看到,<code>ItemTouchHelper</code>内部<br>主要是通过<code>GestureDetectorCompat</code>来处理触摸事件,并通过给<code>Recyclerview</code>设置<code>OnItemTouchListener</code>来实现效果<br>但是遗憾的是其中只 实现了<code>onLongPress</code>的监听.但是为我们设置<code>Recyclerview</code>的Item监听提供了一种新的思路</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//ItemTouchHelper#ItemTouchHelperGestureListener</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ItemTouchHelperGestureListener</span> <span class="keyword">extends</span> <span class="title">GestureDetector</span>.<span class="title">SimpleOnGestureListener</span> </span>&#123;</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">onDown</span><span class="params">(MotionEvent e)</span> </span>&#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><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">onLongPress</span><span class="params">(MotionEvent e)</span> </span>&#123;</span><br><span class="line">      <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过<code>ItemTouchHelper</code>实现<code>Recyclerview</code>的<code>Click</code>,<code>LongClick</code>,<code>Select</code>,<code>Swipe</code>,<code>Drag</code>等效果,<br>主要通过一个叫做<code>ItemTouchHelper.Callback</code>的类,示例:<a href="https://github.com/BoBoMEe/AndroidDev/blob/blog/blogcodes/app/src/main/java/com/bobomee/blogdemos/recycler/helper/RvItemTouchHelperCallback.java" target="_blank" rel="external">RvItemTouchHelperCallback</a></p>
<h2 id="Recyclerview-选中模式"><a href="#Recyclerview-选中模式" class="headerlink" title="Recyclerview 选中模式"></a>Recyclerview 选中模式</h2><p>遗憾的是<code>Recyclerview</code>并不像<code>Listview</code>那样提供<code>selectMode</code>,但是在我们最初不适用<code>selectMode</code>的时候,我们是否采用过一种通过刷新<code>Adapter</code>来实现的方式呢<br>在<code>Recyclerview</code>中要实现选中模式,我们当然也要从<code>Adapter</code>下手,这里可以看一下<a href="http://shawn-duan.com/android/recyclerview/2016/06/14/Multi-Selection-Mode-for-RecyclerView/" target="_blank" rel="external">Multi-Selection Mode for RecyclerView</a><br>其中的核心代码</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="keyword">private</span> SparseBooleanArray mSelectedItems;</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">switchSelectedState</span><span class="params">(<span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (mSelectedItems.get(position)) &#123;       <span class="comment">// item has been selected, de-select it.</span></span><br><span class="line">          mSelectedItems.delete(position);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          mSelectedItems.put(position, <span class="keyword">true</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      notifyItemChanged(position);</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">clearSelectedState</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      List&lt;Integer&gt; selection = getSelectedItems();</span><br><span class="line">      mSelectedItems.clear();</span><br><span class="line">      <span class="keyword">for</span> (Integer i : selection) &#123;</span><br><span class="line">          notifyItemChanged(position);</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<p>可以看到作者也是通过在<code>Adapter</code>中维护一个选中的集合来实现<code>Recyclerview</code>的选中模式的,</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><code>Recyclerview</code>不仅在灵活度和扩展性上都较<code>AbsListView</code>好,在项目中也得到了大量的使用,也还有大量的未知方法等着去实践,<br>比如<code>Recyclerview</code>的<code>Scroll</code>操作,<code>RecycledViewPool</code>.<code>Recyclerview</code>的上下拉刷新,<br>自定义<code>LayoutManager</code>、<code>itemDecoration</code>、<code>itemAnimator</code>等待</p>
<p>参考:<br><a href="http://blog.csdn.net/lmj623565791/article/details/45059587" target="_blank" rel="external"> Android RecyclerView 使用完全解析 体验艺术般的控件</a><br><a href="http://www.devtf.cn/?p=795" target="_blank" rel="external">可拖拽的RecyclerView</a><br><a href="http://shawn-duan.com/android/recyclerview/2016/06/14/Multi-Selection-Mode-for-RecyclerView/" target="_blank" rel="external">Multi-Selection Mode for RecyclerView</a><br><a href="https://guides.codepath.com/android/using-the-recyclerview" target="_blank" rel="external">Using the RecyclerView</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;&lt;code&gt;RecyclerView&lt;/code&gt;出来已经很长时间了,关于其的介绍也非常的多.作为&lt;code&gt;ListView&lt;/code&gt;的升级版,它更加强大和灵活.&lt;br&gt;可以轻松的实现各种布局和动画,见其名,知其意.&lt;code&gt;RecyclerView&lt;/code&gt;用于在有限窗口中展示大量数据集合的可复用的视图.&lt;br&gt;这里主要梳理一下&lt;code&gt;Recyclerview&lt;/code&gt;的常用方法,示例Demo:&lt;a href=&quot;https://github.com/BoBoMEe/AndroidDev/tree/blog/blogcodes/app/src/main/java/com/bobomee/blogdemos/recycler&quot;&gt;BoBoMEe/AndroidDev&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="recyclerview" scheme="http://bobomee.github.io/tags/recyclerview/"/>
    
  </entry>
  
  <entry>
    <title>Android自定义控件细节</title>
    <link href="http://bobomee.github.io/2016/06/25/customview/"/>
    <id>http://bobomee.github.io/2016/06/25/customview/</id>
    <published>2016-06-25T03:48:32.000Z</published>
    <updated>2017-05-14T11:05:17.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>在开发过程中,当系统提供的控件不能满足我们的需求的时候,通常都会采用自定义控件来完成,自定义控件的一般流程:</p>
<p>attrs.xml–&gt;onMeasure()–&gt;onLayout(ViewGroup)–&gt;onDraw()–&gt;onTouchEvent()–&gt;onInterceptTouchEvent(ViewGroup);</p>
<p>其中带有ViewGroup的是自定义ViewGroup需要用到的方法.<br><a id="more"></a></p>
<h2 id="自定义属性"><a href="#自定义属性" class="headerlink" title="自定义属性"></a>自定义属性</h2><p>自定义属性,一般定义用户可自定义的一些特性,关于自定义属性的声明和获取,网上的讲解也比较多了,之前关于自定义属性的总结<a href="http://blog.csdn.net/wbwjx/article/details/50583812" target="_blank" rel="external">Android 自定义属性解析</a>,主要步骤:<br>在values目录下声明相关属性,在自定义控件的构造方法中获取这些属性,应用到自定义控件中.</p>
<h2 id="onMeasure"><a href="#onMeasure" class="headerlink" title="onMeasure"></a>onMeasure</h2><p>onMeasure即测量,测量自定义控件的大小.测量的值是由两部分决定的[Mode和Size],Mode和Size都封装到了一个叫做MeasureSpec的类中;</p>
<p>扩展阅读<a href="http://ghui.me/post/2015/10/view-measure/" target="_blank" rel="external">View的MeasureSpec确定过程</a></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">measureWidth</span><span class="params">(<span class="keyword">int</span> widthMeasureSpec)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int</span> result;</span><br><span class="line">   <span class="keyword">int</span> mode = MeasureSpec.getMode(widthMeasureSpec);</span><br><span class="line">   <span class="keyword">int</span> size = MeasureSpec.getSize(widthMeasureSpec);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mode == MeasureSpec.EXACTLY) &#123;</span><br><span class="line">     <span class="comment">// 如果是精确的,则直接返回结果,</span></span><br><span class="line">     <span class="comment">// 这种模式通常是我们给View设置了一个值,如:android:layout_width="300dp"</span></span><br><span class="line">     <span class="comment">// 或者 android:layout_width="match_parent",屏幕的宽度</span></span><br><span class="line">     result = size;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">     <span class="comment">// MeasureSpec.UNSPECIFIED View的大小没有限制.</span></span><br><span class="line">     <span class="comment">// 通常在ScrollView或者ListView中出现</span></span><br><span class="line">     result = getNeedWidth() + getPaddingLeft() + getPaddingRight();</span><br><span class="line">     <span class="keyword">if</span> (mode == MeasureSpec.AT_MOST) &#123;</span><br><span class="line">       <span class="comment">// 至多不能超过某个值,一般出现在我们设置android:layout_width="wrap_content" 时</span></span><br><span class="line">       <span class="comment">// 设置wrap_content,则View的尺寸由自身内容决定,但最大不能超过父控件的尺寸</span></span><br><span class="line">       <span class="comment">// 这里 即和父控件传入的值 进行比较,取小(最多不能超过size)</span></span><br><span class="line">       result = Math.max(result, size);</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>在得到result 之后需要调用<code>setMeasuredDimension(width,height);</code><br><code>requestLayout();</code>会触发重新测量onMeasure()</p>
<h2 id="onLayout"><a href="#onLayout" class="headerlink" title="onLayout"></a>onLayout</h2><p>onLayout即布局,决定了ViewGroup中子View的显示位置. View中提供了空实现的onLayout,但自定义View一般不需要管,ViewGroup中的onLayout是抽象的,即必须实现.</p>
<p>扩展阅读<a href="http://coderrobin.com/2015/01/26/Android%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%B8%83%E5%B1%80%E4%B9%8BOnLayout/" target="_blank" rel="external">Android自定义控件布局之OnLayout</a></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onLayout</span><span class="params">(<span class="keyword">boolean</span> changed, <span class="keyword">int</span> l, <span class="keyword">int</span> t, <span class="keyword">int</span> r, <span class="keyword">int</span> b)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span>  <span class="keyword">int</span> childCount = getChildCount();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; childCount; i++) &#123;</span><br><span class="line">      <span class="keyword">final</span> View child = getChildAt(i);</span><br><span class="line">      <span class="keyword">if</span> (child.getVisibility() == GONE)&#123;</span><br><span class="line">        <span class="keyword">continue</span>; <span class="comment">//如果child为GONE,则不需要布局</span></span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">int</span> left = caculateChildLeft();<span class="comment">//计算ChildView左上角x坐标</span></span><br><span class="line">      <span class="keyword">int</span> top = caculateChildTop();<span class="comment">//计算ChildView左上角y坐标</span></span><br><span class="line"></span><br><span class="line">      child.layout(left,top,left+childWidth,top+childHeight);<span class="comment">//childWidth 和 childHeight 是child的宽和高</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>尽可能将onMeasure中的一些与Measure无关的操作移动到onLayout中,因为onMeasure在一次自定义控件流程中,可能会执行多次,而onLayout只执行一次.<br><code>requestLayout();</code>会触发重新布局onLayout()</p>
<h2 id="onDraw"><a href="#onDraw" class="headerlink" title="onDraw"></a>onDraw</h2><p>onDraw决定自定义控件的样子,在onDraw()我们只负责View内容的绘制,<br><br>背景系统已经帮我们绘制好了, 这里主要借助的是Canvas这个类.<br><br>同时在onDraw方法中,我们一般会使用translate,rotate,scale,skew等来实现canvas的变换 <br><br>如果使用了变换操作,我们需要使用save()和restore()来保存于恢复canvas的状态<br><br>如果是自定义ViewGroup一般我们也不需要管onDraw这个方法,draw由子控件自己完成</p>
<p>扩展阅读 <a href="http://gold.xitu.io/entry/57465c88c4c971005d6e4422" target="_blank" rel="external">自定义 View 必备－Draw 源码分析及其实践</a></p>
<p><a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1014/1765.html" target="_blank" rel="external">为什么自定义ViewGroup ondraw方法不会被调用</a></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">super</span>.onDraw(canvas);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// allocations per draw cycle.</span></span><br><span class="line">   <span class="keyword">int</span> paddingLeft = getPaddingLeft();</span><br><span class="line">   <span class="keyword">int</span> paddingTop = getPaddingTop();</span><br><span class="line">   <span class="keyword">int</span> paddingRight = getPaddingRight();</span><br><span class="line">   <span class="keyword">int</span> paddingBottom = getPaddingBottom();</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> contentWidth = getWidth() - paddingLeft - paddingRight;</span><br><span class="line">   <span class="keyword">int</span> contentHeight = getHeight() - paddingTop - paddingBottom;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Draw the text.</span></span><br><span class="line">   canvas.drawText(mExampleString, paddingLeft + (contentWidth - mTextWidth) / <span class="number">2</span>,</span><br><span class="line">       paddingTop + (contentHeight + mTextHeight) / <span class="number">2</span>, mTextPaint);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Draw the example drawable on top of the text.</span></span><br><span class="line">   <span class="keyword">if</span> (mExampleDrawable != <span class="keyword">null</span>) &#123;</span><br><span class="line">     mExampleDrawable.setBounds(paddingLeft, paddingTop, paddingLeft + contentWidth,</span><br><span class="line">         paddingTop + contentHeight);</span><br><span class="line">     mExampleDrawable.draw(canvas);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p><code>postInvalidate();</code>和<code>invalidate();</code>会触发重新绘制onDraw().</p>
<h2 id="onTouchEvent"><a href="#onTouchEvent" class="headerlink" title="onTouchEvent"></a>onTouchEvent</h2><p>它是在view中定义的一个方法,用于处理传递到view 的手势事件,<br>其中涉及到几个常量;</p>
<ul>
<li>MotionEvent.ACTION_DOWN: 按下的时候,做一些初始化,赋值操作.</li>
<li>MotionEvent.ACTION_MOVE: 移动的事件</li>
<li>MotionEvent.ACTION_UP: 手指抬起时,做一些释放资源,重置变量的操作</li>
<li>MotionEvent.ACTION_CANCEL:手势释放操作,释放资源,重置变量</li>
<li>MotionEvent.ACTION_POINTER_DOWN: 多点触控操作,需要借助event.getActionIndex(),getPointerId(actionIndex)等一系列方法</li>
<li>MotionEvent.ACTION_POINTER_UP:多点触控非最后一个点被释放时执行,这里需要注意的是activePointer才会起作用</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></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">onSecondaryPointerUp</span><span class="params">(MotionEvent ev)</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">int</span> pointerId = ev.getPointerId(pointerIndex);</span><br><span class="line">       <span class="keyword">if</span> (pointerId == mActivePointerId) &#123;</span><br><span class="line">           <span class="comment">// This was our active pointer going up. Choose a new</span></span><br><span class="line">           <span class="comment">// active pointer and adjust accordingly.</span></span><br><span class="line">           <span class="comment">// <span class="doctag">TODO:</span> Make this decision more intelligent.</span></span><br><span class="line">           <span class="keyword">final</span> <span class="keyword">int</span> newPointerIndex = pointerIndex == <span class="number">0</span> ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">           <span class="comment">//...</span></span><br><span class="line">           mActivePointerId = ev.getPointerId(newPointerIndex);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<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">// 通知父控件不要拦截事件</span></span><br><span class="line"><span class="keyword">final</span> ViewParent parent = getParent();</span><br><span class="line">                    <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        parent.requestDisallowInterceptTouchEvent(<span class="keyword">true</span>);</span><br><span class="line">                    &#125;</span><br></pre></td></tr></table></figure>
<h2 id="onInterceptTouchEvent"><a href="#onInterceptTouchEvent" class="headerlink" title="onInterceptTouchEvent"></a>onInterceptTouchEvent</h2><p>ViewGroup的方法,表示是否拦截事件,其中传递的参数也是MotionEvent,<br>同样包含上面那些常量,我们可以在不同时机调用不同的方法来完成逻辑</p>
<p>扩展阅读 <a href="http://www.jianshu.com/p/e99b5e8bd67b" target="_blank" rel="external">图解 Android 事件分发机制</a></p>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>Configuration: 述设备的配置信息(locale,scaling,..):<a href="https://developer.android.com/reference/android/content/res/Configuration.html" target="_blank" rel="external">官网Configuration介绍</a></p>
<p>ViewConfiguration: 包含了设置UI的超时、大小和距离 de 方法和标准的常量用来,<a href="https://developer.android.com/reference/android/view/ViewConfiguration.html" target="_blank" rel="external">官网ViewConfiguration介绍</a></p>
<p>扩展阅读: <a href="http://souly.cn/%E6%8A%80%E6%9C%AF%E5%8D%9A%E6%96%87/2015/11/26/ViewConfiguration%E5%92%8CConfiguration%E7%B1%BB%E4%BD%BF%E7%94%A8/" target="_blank" rel="external">Viewconfiguration和configuration类使用</a></p>
<p>手势操作GestureDetector: <a href="http://www.tutorialspoint.com/android/android_gestures.htm" target="_blank" rel="external">Android - Gestures Tutorial</a></p>
<p>ViewDragHelper:事件处理,<a href="http://blog.csdn.net/yanbober/article/details/50419059" target="_blank" rel="external">Android应用ViewDragHelper详解及部分源码浅析</a></p>
<p>VelocityTracker: 触摸速率跟踪 <a href="https://developer.android.com/training/gestures/movement.html" target="_blank" rel="external">官网介绍Tracking Movement</a></p>
<p>Scroller: 平滑滚动帮助类 <a href="http://blog.csdn.net/yanbober/article/details/49904715" target="_blank" rel="external"> Android应用开发Scroller详解及源码浅析</a></p>
<p>onSaveInstanceState和onRestoreInstanceState: 存储和恢复自定义控件的状态,参考<a href="http://stormzhang.com/android/2014/09/22/onsaveinstancestate-and-onrestoreinstancestate/" target="_blank" rel="external">onSaveInstanceState &amp; onRestoreInstanceState</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;在开发过程中,当系统提供的控件不能满足我们的需求的时候,通常都会采用自定义控件来完成,自定义控件的一般流程:&lt;/p&gt;
&lt;p&gt;attrs.xml–&amp;gt;onMeasure()–&amp;gt;onLayout(ViewGroup)–&amp;gt;onDraw()–&amp;gt;onTouchEvent()–&amp;gt;onInterceptTouchEvent(ViewGroup);&lt;/p&gt;
&lt;p&gt;其中带有ViewGroup的是自定义ViewGroup需要用到的方法.&lt;br&gt;
    
    </summary>
    
      <category term="自定义控件" scheme="http://bobomee.github.io/categories/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6/"/>
    
    
      <category term="自定义属性" scheme="http://bobomee.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7/"/>
    
  </entry>
  
  <entry>
    <title>android开发之contentprovider</title>
    <link href="http://bobomee.github.io/2016/06/23/contentprovider/"/>
    <id>http://bobomee.github.io/2016/06/23/contentprovider/</id>
    <published>2016-06-23T15:45:36.000Z</published>
    <updated>2017-05-14T11:05:28.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>ContentProvider（内容提供者）是Android中的四大组件之一。主要用于程序间数据的共享(IPC的一种).<br>它提供了一套完整的机制，允许一个程序访问另一个应用程序,并且保证数据的安全性.<br>我们知道在Android中常见的数据存储方式有sharedpreferences,文件和数据库等,但是数据的访问方式会因为存储方式的不同而不同.<br>而且这些数据只能在应用内使用,而ContentProvider允许在程序间实现数据的共享,并且提供好了统一了数据的访问方式.</p>
<a id="more"></a>
<h2 id="ContentResolver"><a href="#ContentResolver" class="headerlink" title="ContentResolver"></a>ContentResolver</h2><p>访问内容提供器中共享的数据,需要借助通过ContentResolver,其中context.getContentResolver()方法可以获取到ContentResolver实例,<br>ContentResolver提供了一系列方法来对数据进行CRUD操作.</p>
<h2 id="URI"><a href="#URI" class="headerlink" title="URI"></a>URI</h2><p>统一资源标识符,结构和HTTP形式的URL是一样的.<br>代表了要操作的数据,每一个ContentProvider都拥有一个公共的URI,不能重复<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">&lt;prefix&gt;://&lt;authority&gt;/&lt;data_type&gt;/&lt;id&gt;</span><br></pre></td></tr></table></figure></p>
<p>其中,<code>&lt;prefix&gt;</code>的值永远为<code>content</code>,不可更改</p>
<p><code>&lt;authority&gt;</code>唯一标识符,用来定位<code>ContentProvider</code>,一般为自定义Provider的全路径名,保证唯一</p>
<p><code>&lt;data_type&gt;/&lt;id&gt;</code>代表资源路径,如<code>/person/2</code>代表person表中id为2的数据</p>
<h2 id="MIME"><a href="#MIME" class="headerlink" title="MIME"></a>MIME</h2><p>用于指定某个扩展名的文件用一种应用程序来打开,如application/pdf,text/css等,Android中的MIME类型有两种</p>
<ul>
<li>多条记录<br>vnd.android.cursor.dir/vnd.xxx</li>
<li>单条记录<br>vnd.android.cursor.item/vnd.xxx</li>
</ul>
<p>其中包含类型和子类型,/前面的类型是Android定义好的,不可更改,子类型中vnd.后面的可以随意指定.在使用Intent时，会用到MIME这玩意，根据Mimetype打开符合条件的活动。</p>
<h2 id="自定义Contentprovider"><a href="#自定义Contentprovider" class="headerlink" title="自定义Contentprovider"></a>自定义Contentprovider</h2><ul>
<li>继承ContentProvider,并重写其中的一些方法,</li>
</ul>
<p>其中OnCreate()方法在Provider初始化的时候执行</p>
<p>getType（URI uri）用于返回某一URI对应的MIME类型,运行在Binder线程池中</p>
<p>还有其他一些操作CRUD操作,运行在Binder线程池中</p>
<ul>
<li>UriMatcher</li>
</ul>
<p>用于冠关联Provider中的自定义URI和URI_CODE,通过addURI（）将相应的Path和Code联系起来,通过match(uri)得到相应的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><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><br><span class="line"><span class="comment">//为外部程序准备好一个所有地址匹配集合</span></span><br><span class="line"><span class="keyword">static</span>&#123;</span><br><span class="line">      uriMatcher = <span class="keyword">new</span> UriMatcher(UriMatcher.NO_MATCH);</span><br><span class="line">      uriMatcher.addURI(PROVIDER_NAME, <span class="string">"students"</span>, STUDENTS);</span><br><span class="line">      uriMatcher.addURI(PROVIDER_NAME, <span class="string">"students/#"</span>, STUDENT_ID);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 取得数据的类型,此方法会在系统进行URI的MIME过滤时被调用。</span></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">getType</span><span class="params">(Uri uri)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (uriMatcher.match(uri))&#123;</span><br><span class="line">       <span class="comment">/**</span><br><span class="line">       * Get all student records</span><br><span class="line">       */</span></span><br><span class="line">       <span class="keyword">case</span> STUDENTS:</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"vnd.android.cursor.dir/vnd.example.students"</span>;</span><br><span class="line"></span><br><span class="line">       <span class="comment">/**</span><br><span class="line">       * Get a particular student</span><br><span class="line">       */</span></span><br><span class="line">       <span class="keyword">case</span> STUDENT_ID:</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"vnd.android.cursor.item/vnd.example.students"</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">default</span>:</span><br><span class="line">       <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Unsupported URI: "</span> + uri);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>ContentUris<br>用于操作Uri路径后面的ID部分</li>
</ul>
<p>其中withAppendedId(uri, id)用于为路径加上ID部分：</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">Uri uri = Uri.parse(<span class="string">"content://com.example.provider.College/students"</span>)  </span><br><span class="line">Uri resultUri = ContentUris.withAppendedId(uri, <span class="number">10</span>);   </span><br><span class="line"><span class="comment">//生成后的Uri为：content://com.example.provider.College/students/10</span></span><br></pre></td></tr></table></figure>
<p>parseId(uri)方法用于从路径中获取ID部分：</p>
<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">Uri uri = Uri.parse(<span class="string">"content://com.example.provider.College/students/10 "</span>)  </span><br><span class="line"><span class="keyword">long</span> personid = ContentUris.parseId(uri);<span class="comment">//获取的结果为:10</span></span><br></pre></td></tr></table></figure>
<ul>
<li>配置清单文件</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></pre></td><td class="code"><pre><span class="line">&lt;provider</span><br><span class="line">        android:authorities=<span class="string">"com.bobomee.android.contentproviderdemo"</span></span><br><span class="line">        android:name=<span class="string">".BookContentProvider"</span>/&gt;</span><br></pre></td></tr></table></figure>
<h2 id="监听ContentProvider变化"><a href="#监听ContentProvider变化" class="headerlink" title="监听ContentProvider变化"></a>监听ContentProvider变化</h2><p>如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化，可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者，</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></pre></td><td class="code"><pre><span class="line">Uri insertUri = Uri.withAppendedPath(uri, <span class="string">"/"</span> + rowId);</span><br><span class="line">      Log.i(TAG, <span class="string">"insertUri:"</span>+insertUri.toString());</span><br><span class="line">      <span class="comment">//发出数据变化通知(book表的数据发生变化)</span></span><br><span class="line">      getContext().getContentResolver().notifyChange(uri, <span class="keyword">null</span>);</span><br><span class="line">      <span class="keyword">return</span> insertUri;</span><br></pre></td></tr></table></figure>
<p>使用Demo:<a href="https://github.com/BoBoMEe/AndroidDev/tree/provider" target="_blank" rel="external">ProviderDemo</a></p>
<p>参考文章:</p>
<p><a href="http://blog.csdn.net/xyz_lmn/article/details/7161635" target="_blank" rel="external">ContentProvider和Uri详解</a></p>
<p><a href="http://www.tutorialspoint.com/android/android_content_providers.htm" target="_blank" rel="external">Android - Content Providers</a></p>
<p><a href="http://www.imooc.com/article/8001" target="_blank" rel="external">我是这样理解Android开发中的contentprovider的</a></p>
<p><a href="https://liuzhichao.com/p/562.html" target="_blank" rel="external">Android开发教程：Android数据储存之ContentProvider</a></p>
<p><a href="http://codingnow.cn/android/1078.html" target="_blank" rel="external">android ContentProvider使用详解</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;ContentProvider（内容提供者）是Android中的四大组件之一。主要用于程序间数据的共享(IPC的一种).&lt;br&gt;它提供了一套完整的机制，允许一个程序访问另一个应用程序,并且保证数据的安全性.&lt;br&gt;我们知道在Android中常见的数据存储方式有sharedpreferences,文件和数据库等,但是数据的访问方式会因为存储方式的不同而不同.&lt;br&gt;而且这些数据只能在应用内使用,而ContentProvider允许在程序间实现数据的共享,并且提供好了统一了数据的访问方式.&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Provider" scheme="http://bobomee.github.io/tags/Provider/"/>
    
  </entry>
  
  <entry>
    <title>浅谈Android架构之MVP,MVVM</title>
    <link href="http://bobomee.github.io/2016/06/09/android-mvp-md/"/>
    <id>http://bobomee.github.io/2016/06/09/android-mvp-md/</id>
    <published>2016-06-09T03:48:32.000Z</published>
    <updated>2017-05-14T11:05:31.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p><code>MVP(Model-View-Presenter)</code>是传统<code>MVC(Model-View-Controller)</code>在Android开发上的一种变种、进化模式。主要用来隔离UI、UI逻辑和业务逻辑、数据,创建松散耦合并可重用的对象。</p>
<p>我们知道View层是容易变化且多种多样的,业务逻辑也是多种多样的,与传统的MVC相比,P充当了C的作用.<br>Model存储数据，View表示Model的表现，Presenter协调两者之间的通信.</p>
<p>而后在MVP基础上也出现了一些变种,如MVVM,MVPVM等,相比较MVP而言,MVVM使数据绑定变得更加简单.MVPVM在MVVM中加入引入Presenter层</p>
<a id="more"></a>
<h2 id="MVC-MVP-MVVM-MVPVM图解"><a href="#MVC-MVP-MVVM-MVPVM图解" class="headerlink" title="MVC,MVP,MVVM,MVPVM图解"></a>MVC,MVP,MVVM,MVPVM图解</h2><p>这里感谢源博客和图片提供者.</p>
<h3 id="MVC"><a href="#MVC" class="headerlink" title="MVC:"></a>MVC:</h3><p><img src="http://img.blog.csdn.net/20160609230144717" alt="MVC_Android MVP模式 简单易懂的介绍方式"></p>
<p>MVC中View接受事件,并调用Controller来操作Model,同时,当Model实例的数据发生变化后，Controller再更新界面(当然View也可以直接更新Model)。</p>
<p>在传统的开发中Activity俨然既充当了Controller又充当了View的作用.既需要接受用户响应操作Model,又要更新界面.<br>这样做有一个好处就是数据的更新变得很简单,但是缺点也十分明显,Activity是非臃肿,后期不好维护.</p>
<h3 id="MVP"><a href="#MVP" class="headerlink" title="MVP:"></a>MVP:</h3><p><img src="http://img.blog.csdn.net/20160609230312969" alt="MVP_Android MVP模式 简单易懂的介绍方式"></p>
<p>MVP中将业务逻辑单独抽出Presenter,View层变成一个被动的东西,Presenter负责完成View层与Model层的交互.<br>View 不可以直接和Model交互(MVC中允许Model和View交互),只有Presenter告知其更新，它才会去更新.<br>而且Presenter和View的交互是通过接口来完成.</p>
<h3 id="MVVM"><a href="#MVVM" class="headerlink" title="MVVM:"></a>MVVM:</h3><p><img src="http://img.blog.csdn.net/20160609230346176" alt="MVVM_MVVM_Android-CleanArchitecture"></p>
<p>MVVM在Android上对应data binding,MVVM最先使用在WPF中,通过ViewModel和View的映射,完成了View和Model的双向绑定.<br>View的事件直接传递到ViewModel，ViewModel去对Model进行操作并接受更新.进而反馈到View上.<br>因为ViewModel与View的耦合,MVVM有一个缺点就是View的复用问题,<br>因为去掉了Presenter,View层依然过重.</p>
<h3 id="MVPVM"><a href="#MVPVM" class="headerlink" title="MVPVM:"></a>MVPVM:</h3><p><img src="http://img.blog.csdn.net/20160609230425176" alt="MVPVM_从零开始的Android新项目3 - MVPVM in Action, 谁告诉你MVP和MVVM是互斥的"></p>
<p>MVPVM是MVP和MVVM的演化版本,降低了ViewModel与View的耦合,View只需要实现ViewModel的观察者接口实现更新.ViewModel不再对Model直接进行操作,而是交给了Presenter.Presenter操作Model并反馈到ViewModel上<br>Model,View,ViewModel之间通过Presenter联系了起来.</p>
<h2 id="MVP实践"><a href="#MVP实践" class="headerlink" title="MVP实践"></a>MVP实践</h2><p>Google官方<a href="https://github.com/googlesamples/android-architecture" target="_blank" rel="external">android-architecture </a>无疑是学习MVP最佳资料,官方项目展示了通过不同方式来实现MVP,其中展示了通过basic,loaders,data binding,clean,dagger,content provider,rxjava等不同方式来实现相同的功能,当然我们只要掌握其精髓,触类旁通就可以</p>
<h3 id="看官方MVP的体会"><a href="#看官方MVP的体会" class="headerlink" title="看官方MVP的体会"></a>看官方MVP的体会</h3><ul>
<li><p>单独模块抽取<code>IContract</code>接口管理<code>IView</code>和 <code>Presenter</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></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">AddEditTaskContract</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">interface</span> <span class="title">View</span> <span class="keyword">extends</span> <span class="title">BaseView</span>&lt;<span class="title">Presenter</span>&gt; </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="class"><span class="keyword">interface</span> <span class="title">Presenter</span> <span class="keyword">extends</span> <span class="title">BasePresenter</span> </span>&#123;</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>当Fragment作为<code>View</code>的时候,Activity负责创建<code>IView</code>和<code>Presenter</code>实例,并将二者关联起来,官方的这幅图即可说明</p>
</li>
</ul>
<p><img src="http://img.blog.csdn.net/20160609230727130" alt="fragment_mvp"></p>
<p>代码说明:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//todo-mvp$TasksActivity</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">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.tasks_act);</span><br><span class="line">       <span class="comment">// ...</span></span><br><span class="line">       TasksFragment tasksFragment =</span><br><span class="line">               (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);</span><br><span class="line">       <span class="comment">//...</span></span><br><span class="line">       mTasksPresenter = <span class="keyword">new</span> TasksPresenter(</span><br><span class="line">               Injection.provideTasksRepository(getApplicationContext()), tasksFragment);</span><br><span class="line">       <span class="comment">//...</span></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><p><code>IPresenter</code>由具体的<code>Presenter</code>实现,<code>IView</code>由View层(<code>Activity/Fragment</code>)实现,<code>IView</code>实现类中包含了<code>Presenter</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></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">BaseView</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">      <span class="comment">// View中保持对Presenter的引用。</span></span><br><span class="line">      <span class="function"><span class="keyword">void</span> <span class="title">setPresenter</span><span class="params">(T presenter)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>同时在<code>Presenter</code>构造时需要传入<code>IView</code>对象(用于更新<code>View</code>).</p>
</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></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">TasksPresenter</span> <span class="keyword">implements</span> <span class="title">TasksContract</span>.<span class="title">Presenter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> TasksRepository mTasksRepository;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> TasksContract.View mTasksView;</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">TasksPresenter</span><span class="params">(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView)</span> </span>&#123;</span><br><span class="line">        mTasksRepository = checkNotNull(tasksRepository, <span class="string">"tasksRepository cannot be null"</span>);</span><br><span class="line">        mTasksView = checkNotNull(tasksView, <span class="string">"tasksView cannot be null!"</span>);</span><br><span class="line"><span class="comment">//setPresenter</span></span><br><span class="line">        mTasksView.setPresenter(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><code>Model</code>不仅仅是<code>JavaBean</code>,而且拥有操作数据的业务逻辑(获取、存储、更新),同时<code>Model</code>也可以将业务抽成接口,这样就可以随意拓展数据源</li>
</ul>
<h2 id="MVP变种"><a href="#MVP变种" class="headerlink" title="MVP变种"></a>MVP变种</h2><p>MVP架构的好处就不多说了,但是使用<code>Activity/Fragment</code>作为<code>View</code>层有如下问题,<br>参考<a href="http://www.devtf.cn/?p=27" target="_blank" rel="external">一种在android中实现MVP模式的新思路</a></p>
<ul>
<li>当内存不足,Activity被回收后,这使得状态的保存和恢复成为问题,因为涉及到了Model操作.</li>
<li><p>生命周期的控制问题也很麻烦,需要在Presenter中写一大堆和生命周期相关的接口规范</p>
</li>
<li><p>Activity中包含了很多系统服务,逻辑操作方便</p>
</li>
</ul>
<h3 id="使用Activity-Fragment作为Presenter"><a href="#使用Activity-Fragment作为Presenter" class="headerlink" title="使用Activity/Fragment作为Presenter"></a>使用Activity/Fragment作为Presenter</h3><p><code>Activity/Fragment</code>中的系统服务和业务逻辑紧密相关.理想的状态是View不涉及到逻辑操作.</p>
<p><code>Activity/Fragment</code>作为Presenter,需要将其UI逻辑抽取到一个单独的类中来管理.</p>
<p>UI逻辑接口<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></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">Vu</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inflate</span><span class="params">(LayoutInflater inflater, ViewGroup container, Bundle bundle)</span></span>;</span><br><span class="line">    <span class="function">View <span class="title">getView</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>作为Presenter的BaseActivity,覆盖了所有生命周期方法</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></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">BasePresenterActivity</span>&lt;<span class="title">V</span> <span class="keyword">extends</span> <span class="title">Vu</span>&gt; <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> V vu;</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">final</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">        <span class="keyword">try</span> &#123;</span><br><span class="line">            vu = getVuClass().newInstance();</span><br><span class="line">            vu.inflate(getLayoutInflater(), <span class="keyword">null</span>,<span class="keyword">null</span>);</span><br><span class="line">            setContentView(vu.getView());</span><br><span class="line">            onBindVu();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InstantiationException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        onDestroyVu();</span><br><span class="line">        vu = <span class="keyword">null</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="function"><span class="keyword">protected</span> <span class="keyword">abstract</span> Class&lt;V&gt; <span class="title">getVuClass</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onBindVu</span><span class="params">()</span></span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDestroyVu</span><span class="params">()</span> </span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="举个例子"><a href="#举个例子" class="headerlink" title="举个例子:"></a>举个例子:</h3><p>具体的UI逻辑,不论Presenter变为Activity还是Fragment都不用改变.在周期方法中改变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></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">HelloVu</span> <span class="keyword">implements</span> <span class="title">Vu</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">View view;</span><br><span class="line">TextView helloView;</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">(LayoutInflater inflater, ViewGroup container)</span> </span>&#123;</span><br><span class="line">    view = inflater.inflate(R.layout.hello, container, <span class="keyword">false</span>);</span><br><span class="line">    helloView = (TextView) view.findViewById(R.id.hello);</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> View <span class="title">getView</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> view;</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">setHelloMessage</span><span class="params">(String msg)</span></span>&#123;</span><br><span class="line">    helloView.setText(msg);</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>具体的Presenter<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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloActivity</span> <span class="keyword">extends</span> <span class="title">BasePresenterActivity</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">onBindVu</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    vu.setHelloMessage(<span class="string">"Hello World!"</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> Class&lt;MainVu&gt; <span class="title">getVuClass</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> HelloVu.class;</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>
<h2 id="MVVM-数据的动态绑定"><a href="#MVVM-数据的动态绑定" class="headerlink" title="MVVM:数据的动态绑定"></a>MVVM:数据的动态绑定</h2><p>官方文档:<br><a href="https://developer.android.com/topic/libraries/data-binding/index.html" target="_blank" rel="external">https://developer.android.com/topic/libraries/data-binding/index.html</a><br>使用Data Binding后,我们的xml和之前是不太一样的.抄袭自官方文档</p>
<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><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">&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"&gt;</span><br><span class="line">    &lt;data&gt;</span><br><span class="line">        &lt;variable name="user" type="com.example.User"/&gt;</span><br><span class="line">    &lt;/data&gt;</span><br><span class="line">    &lt;LinearLayout&gt;</span><br><span class="line">    ....</span><br><span class="line">    &lt;/LinearLayout&gt;</span><br><span class="line">&lt;/layout&gt;</span><br></pre></td></tr></table></figure>
<p>相应的Activity的setContentView也会变化</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></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">   MainActivityBinding binding = DataBindingUtil.setContentView(<span class="keyword">this</span>, R.layout.main_activity);</span><br><span class="line">   User user = <span class="keyword">new</span> User(<span class="string">"Test"</span>, <span class="string">"User"</span>);</span><br><span class="line">   binding.setUser(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里一个JavaBean对应一个xml布局文件,View的复用变得很困难.</p>
<h2 id="TheMVP-MVVM和MVP的结合"><a href="#TheMVP-MVVM和MVP的结合" class="headerlink" title="TheMVP:MVVM和MVP的结合"></a>TheMVP:MVVM和MVP的结合</h2><p>TheMVP解决了这个问题,通过在Presenter中定义ViewModel接口,实现数据的双向绑定与MVP的结合.<br>项目地址: <a href="https://github.com/kymjs" target="_blank" rel="external">kymjs</a>/<strong><a href="https://github.com/kymjs/TheMVP" target="_blank" rel="external">TheMVP</a></strong></p>
<p>核心代码:这里的IDelegate相当与上面的Vu<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></pre></td><td class="code"><pre><span class="line"><span class="comment">//ViewModel接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">DataBinder</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">IDelegate</span>, <span class="title">D</span> <span class="keyword">extends</span> <span class="title">IModel</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 将数据与View绑定，这样当数据改变的时候，框架就知道这个数据是和哪个View绑定在一起的，就可以自动改变ui</span><br><span class="line">     * 当数据改变的时候，会回调本方法。</span><br><span class="line">     *</span><br><span class="line">     * <span class="doctag">@param</span> viewDelegate 视图层代理</span><br><span class="line">     * <span class="doctag">@param</span> data         数据模型对象</span><br><span class="line">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">viewBindModel</span><span class="params">(T viewDelegate, D data)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Presenter:在数据改变的时候调用notifyModelChanged()更新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></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">DataBindActivity</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">IDelegate</span>&gt; <span class="keyword">extends</span></span><br><span class="line">        <span class="title">ActivityPresenter</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> DataBinder binder;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> DataBinder <span class="title">getDataBinder</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> &lt;D extends IModel&gt; <span class="function"><span class="keyword">void</span> <span class="title">notifyModelChanged</span><span class="params">(D data)</span> </span>&#123;</span><br><span class="line">        binder.viewBindModel(viewDelegate, data);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="MVPVM-View复用与瘦身"><a href="#MVPVM-View复用与瘦身" class="headerlink" title="MVPVM:View复用与瘦身"></a>MVPVM:View复用与瘦身</h2><p>在MVPVM作者的介绍中.</p>
<ul>
<li>Model层,和MVP中的Model是类似的.即PO或者DO</li>
<li>View层,依然是由Activity/Fragment来担当,不同的是需要实现ViewModel的观察者接口,来实现View的动态更新</li>
<li>Presenter层,如上图所示,Presenter作为核心,连接着M,V,VM</li>
<li>VM层,和MVVM中的VM是类似的,只是为了让VM可以重用.不再和特定的View绑定,而且不再直接对Model进行操作.</li>
</ul>
<p>详见:<a href="http://blog.zhaiyifan.cn/2016/03/16/android-new-project-from-0-p3/" target="_blank" rel="external">从零开始的Android新项目3 - MVPVM in Action, 谁告诉你MVP和MVVM是互斥的</a></p>
<p>最后附上用MVP写的一个小Demo:<br><a href="https://github.com/BoBoMEe/AndroidDev/tree/mvp/MVP" target="_blank" rel="external">MVP</a></p>
<p>参考:<br><a href="http://www.devtf.cn/?p=27" target="_blank" rel="external">一种在android中实现MVP模式的新思路</a></p>
<p><a href="http://kaedea.com/2015/10/11/android-mvp-pattern/" target="_blank" rel="external">ANDROID MVP模式 简单易懂的介绍方式</a></p>
<p><a href="http://rocko.xyz/2015/11/07/MVVM_Android-CleanArchitecture/" target="_blank" rel="external">MVVM_Android-CleanArchitecture</a></p>
<p><a href="http://blog.zhaiyifan.cn/2016/03/16/android-new-project-from-0-p3/#rd?sukey=ecafc0a7cc4a741b87b0faa09babd83d0aaade29e75380871b478818ebd3831b8e0f7185e732c99cdeaa2efe0922b0f8" target="_blank" rel="external">从零开始的Android新项目3 - MVPVM in Action, 谁告诉你MVP和MVVM是互斥的</a></p>
<p><a href="http://kymjs.com/code/2015/11/09/01" target="_blank" rel="external">用MVP架构开发Android应用</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;&lt;code&gt;MVP(Model-View-Presenter)&lt;/code&gt;是传统&lt;code&gt;MVC(Model-View-Controller)&lt;/code&gt;在Android开发上的一种变种、进化模式。主要用来隔离UI、UI逻辑和业务逻辑、数据,创建松散耦合并可重用的对象。&lt;/p&gt;
&lt;p&gt;我们知道View层是容易变化且多种多样的,业务逻辑也是多种多样的,与传统的MVC相比,P充当了C的作用.&lt;br&gt;Model存储数据，View表示Model的表现，Presenter协调两者之间的通信.&lt;/p&gt;
&lt;p&gt;而后在MVP基础上也出现了一些变种,如MVVM,MVPVM等,相比较MVP而言,MVVM使数据绑定变得更加简单.MVPVM在MVVM中加入引入Presenter层&lt;/p&gt;
    
    </summary>
    
      <category term="架构设计" scheme="http://bobomee.github.io/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
    
    
      <category term="mvp" scheme="http://bobomee.github.io/tags/mvp/"/>
    
  </entry>
  
  <entry>
    <title>Android:Dagger2学习之由浅入深</title>
    <link href="http://bobomee.github.io/2016/06/05/dagger2basic/"/>
    <id>http://bobomee.github.io/2016/06/05/dagger2basic/</id>
    <published>2016-06-05T15:02:14.000Z</published>
    <updated>2017-05-14T11:05:51.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>Dagger2是一款使用在Java和Android上的静态的,运行时依赖注入框架.官方地址:<a href="http://google.github.io/dagger/" target="_blank" rel="external">http://google.github.io/dagger/</a></p>
<p>记得当初刚学习Dagger2的时候看了许多博客,但是感觉上手依然困难,所谓光学不练就是这个意思吧</p>
<p>时至今日,用上此框架的同仁越来越多.分析文章也很多,上手相对要简单了许多.</p>
<p>学习Dagger2最先要明白的是其各个注解的含义及工作原理,这样才可以快速的上手和使用.</p>
<p>在这里简要记录一下在使用Dagger2过程中的感受和心得体会.</p>
<p>本文示例代码地址:<a href="https://github.com/BoBoMEe/AndroidDev/tree/dagger2/Dagger2Sample" target="_blank" rel="external">Dagger2Sample</a></p>
<a id="more"></a>
<h2 id="配置信息"><a href="#配置信息" class="headerlink" title="配置信息"></a>配置信息</h2><p>首先贴出此篇博客的所有依赖配置信息,因为dagger2需要依赖 apt,所以也需要引入apt插件,</p>
<ul>
<li>project  下面的build.gradle文件配置</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">buildscript &#123;</span><br><span class="line">  repositories &#123;</span><br><span class="line">    jcenter()</span><br><span class="line">    <span class="comment">//依赖maven库</span></span><br><span class="line">    mavenCentral()</span><br><span class="line">  &#125;</span><br><span class="line">  dependencies &#123;</span><br><span class="line">    classpath <span class="string">'com.android.tools.build:gradle:2.1.2'</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// <span class="doctag">NOTE:</span> Do not place your application dependencies here; they belong</span></span><br><span class="line">    <span class="comment">// in the individual module build.gradle files</span></span><br><span class="line">    classpath <span class="string">'com.neenbedankt.gradle.plugins:android-apt:1.8'</span></span><br><span class="line">    classpath <span class="string">'me.tatarka:gradle-retrolambda:3.2.5'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">allprojects &#123;</span><br><span class="line">  repositories &#123;</span><br><span class="line">    jcenter()</span><br><span class="line">    mavenCentral()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>app目录下的build.gradle文件配置</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></pre></td><td class="code"><pre><span class="line">apply plugin: <span class="string">'me.tatarka.retrolambda'</span> <span class="comment">//使用lanbda表达式</span></span><br><span class="line">apply plugin: <span class="string">'com.neenbedankt.android-apt'</span> <span class="comment">//apt插件</span></span><br><span class="line"></span><br><span class="line">android &#123;</span><br><span class="line">  <span class="comment">//...</span></span><br><span class="line">  <span class="comment">// 注释冲突</span></span><br><span class="line">  packagingOptions &#123;</span><br><span class="line">    exclude <span class="string">'META-INF/services/javax.annotation.processing.Processor'</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 使用Java1.8</span></span><br><span class="line">  compileOptions &#123;</span><br><span class="line">    sourceCompatibility JavaVersion.VERSION_1_8</span><br><span class="line">    targetCompatibility JavaVersion.VERSION_1_8</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">dependencies &#123;</span><br><span class="line">  <span class="function">compile <span class="title">fileTree</span><span class="params">(dir: <span class="string">'libs'</span>, include: [<span class="string">'*.jar'</span>])</span></span><br><span class="line">  testCompile 'junit:junit:4.12'</span><br><span class="line">  compile 'com.android.support:appcompat-v7:23.4.0'</span><br><span class="line"></span><br><span class="line">  apt 'com.google.dagger:dagger-compiler:2.0.2'</span><br><span class="line">  compile 'com.google.dagger:dagger:2.0.2'</span><br><span class="line">  provided 'javax.annotation:jsr250-api:1.0'</span><br><span class="line"></span><br><span class="line">  compile 'io.reactivex:rxandroid:1.1.0' <span class="comment">// RxAndroid</span></span><br><span class="line">  compile 'io.reactivex:rxjava:1.1.0' <span class="comment">// RxJava</span></span><br><span class="line"></span><br><span class="line">  compile 'com.squareup.retrofit2:retrofit:2.0.2' <span class="comment">// Retrofit网络处理</span></span><br><span class="line">  compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' <span class="comment">// Retrofit的rx解析库</span></span><br><span class="line">  compile 'com.squareup.retrofit2:converter-gson:2.0.2' <span class="comment">// Retrofit的gson库</span></span><br><span class="line"></span><br><span class="line">  compile 'com.jakewharton:butterknife:8.0.1' <span class="comment">// 标注</span></span><br><span class="line">  apt 'com.jakewharton:butterknife-compiler:8.0.1' <span class="comment">//视图注入</span></span><br><span class="line"></span><br><span class="line">&#125;</span></span><br></pre></td></tr></table></figure>
<h2 id="Dagger2基础注解"><a href="#Dagger2基础注解" class="headerlink" title="Dagger2基础注解"></a>Dagger2基础注解</h2><p>Inject，Component，Module，Provides是Dagger中最基础的几个注解,是整个依赖注入的核心,下面我们来看一下各个注解的作用.</p>
<h3 id="Inject-and-Component"><a href="#Inject-and-Component" class="headerlink" title="Inject and Component"></a>Inject and Component</h3><p><code>@Inject</code>:</p>
<p>用来标注<code>需要依赖的成员</code> 和 <code>被依赖类的构造函数</code>(如果依赖类同时依赖了其他类,其他类的构造函数也要有<code>@Inject</code>标注),</p>
<p><strong>注意:</strong> 使用<code>@Inject</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Inject</span> <span class="function"><span class="keyword">public</span> <span class="title">UserModel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = <span class="string">"Hello Dagger2,I`m from Inject"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Inject</span> <span class="function"><span class="keyword">public</span> <span class="title">UserModel</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面的写法会报错:<code>Error:(29, 18) 错误: Types may only contain one @Inject constructor.</code></p>
<p><code>@Component</code>:</p>
<p>光有<code>@Inject</code>可不行,还需要一个东西将他两联系起来才能让<code>依赖类</code>找到<code>被依赖对象</code>,其中<code>Component</code>就起到了这个作用,在<code>Dagger2</code>中是以接口形式存在,<br>是用来连接 <code>需要依赖类</code> 和 <code>被依赖类</code>,<code>Component</code>使用<code>injectXX(XX xx)</code>将依赖注入到需要依赖的地方,</p>
<h4 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h4><p>接下来我们来看一下Dagger2最基本的用法</p>
<p>第一步:编写JavaBean<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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午8:57.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserModel</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Inject</span> <span class="function"><span class="keyword">public</span> <span class="title">UserModel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">"Hello Dagger2,I`m from Inject"</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> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> name;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>第二步:创建Component<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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午8:59.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Component</span> <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserComponent</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity mainActivity)</span></span>;</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">Initializer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Initializer</span><span class="params">()</span> </span>&#123;</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> UserComponent <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> DaggerUserComponent.create();</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>第三步:构建依赖,先build一下生成dagger图谱<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="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="meta">@BindView</span>(R.id.text) TextView text;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Inject</span> UserModel userModel;</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">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">    ButterKnife.bind(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//inject dependencies</span></span><br><span class="line">    UserComponent.Initializer.init().inject(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">    text.setText(userModel.getName());</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>注意:</strong> <code>@Inject</code>成员不能是<code>private</code>的,否则会报:<code>Error:(35, 29) 错误: Dagger does not support injection into private fields</code></p>
<h3 id="Provides-and-Module"><a href="#Provides-and-Module" class="headerlink" title="Provides and Module"></a>Provides and Module</h3><p><code>@Provides</code>:</p>
<p>自定义依赖,Dagger2中不仅提供了<code>@Inject</code>标注,他比<code>@Inject</code>更加强大,不仅可以提供本地依赖,还可以提供第三方依赖(<code>第三方库和Android系统类</code>不可以用<code>@Inject</code>注解构造函数).</p>
<p><strong>注意:</strong> <code>@Provides</code>的优先级高于<code>@Inject</code></p>
<p><code>@Module</code>:</p>
<p>所有的<code>@Provides</code>都必须包含在<code>@Module</code>内部,相当于简单工厂,提供了各种依赖<code>@Provides</code>方法.之后再将Module加入到Component管理即可完成依赖注入(Component不仅可以从<code>@Inject</code>找到被依赖类,还可以从<code>@Module</code>找到被依赖类)</p>
<p><strong>注意:</strong> 通过modules列出一个Component所有依赖的Module,如果缺失任何一个编译会报错</p>
<h4 id="自定义Module使用"><a href="#自定义Module使用" class="headerlink" title="自定义Module使用"></a>自定义Module使用</h4><p>第一步:编写AppModule,提供依赖<code>@Provide</code>方法,其中<code>@Singleton</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><br><span class="line"> * Created on 16/6/8.下午9:30.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Module</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AppModule</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> App app;<span class="comment">//App为我们自定义的Application</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">AppModule</span><span class="params">(App app)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.app = app;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Provides</span> <span class="meta">@Singleton</span> <span class="function"><span class="keyword">public</span> App <span class="title">provideApp</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> app;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>第二步:创建Component,这里使用了<code>@Singleton</code>,必须和Module中<code>@Provides</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><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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:31.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Singleton</span> <span class="meta">@Component</span>(modules = &#123;</span><br><span class="line">    AppModule.class</span><br><span class="line">&#125;) <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">AppComponent</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//将依赖注入到自定义的Application</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(App app)</span></span>;</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">Initializer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Initializer</span><span class="params">()</span> </span>&#123;</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> AppComponent <span class="title">init</span><span class="params">(App app)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> DaggerAppComponent.builder().appModule(<span class="keyword">new</span> AppModule(app)).build();</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>第三步:自定义Application中完成依赖</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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:29.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> AppComponent appComponent;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Inject</span> <span class="keyword">static</span> App app;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> App <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> app;</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">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><br><span class="line"><span class="comment">//当inject完成后app就不为空了,且和Application生命周期相同,因此//Singleton可以起到全局单例的作用</span></span><br><span class="line">    buildComponent();</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">buildComponent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    appComponent = AppComponent.Initializer.init(<span class="keyword">this</span>);</span><br><span class="line">    appComponent.inject(<span class="keyword">this</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//向外提供appComponent,方便其他依赖appComponent的component构建</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> AppComponent <span class="title">component</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> appComponent;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>第四步:使用依赖</p>
<figure class="highlight java"><table><tr><td class="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">//MainActivity中</span></span><br><span class="line"> <span class="meta">@BindView</span>(R.id.text1) TextView text1;</span><br><span class="line"></span><br><span class="line"> text1.setText(App.get().toString());</span><br></pre></td></tr></table></figure>
<h2 id="Component组织方式"><a href="#Component组织方式" class="headerlink" title="Component组织方式"></a>Component组织方式</h2><ol>
<li><p>一个应用中,必须包含一个全局的Component(类似于上面的AppComponent),管理整个App的实例.</p>
</li>
<li><p>一般AppComponent和Application生命周期相同,所以注入到Application中即可.</p>
</li>
<li><p>因为Application是全局单例的,所以AppModule中创建的实例也是单例的(<code>@Singleton</code>注解就是依照此原理).</p>
</li>
<li><p>根据具体功能或者单独的一个页面定义一个Component.</p>
</li>
<li><p>某个单独的Component要用到全局实例的时候,可以通过继承AppComponent来实现</p>
</li>
<li><p>Component之间的关系有 <code>依赖(dependencies)</code>,<code>包含(SubComponent)</code>,<code>继承方式(extends)</code></p>
</li>
</ol>
<h3 id="Component依赖写法"><a href="#Component依赖写法" class="headerlink" title="Component依赖写法"></a>Component依赖写法</h3><p>接下来来看一下一个典型的dependencies写法</p>
<p>第一步: 定义注入到MainActivity的Product</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:48.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Product</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> String productQualifier;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Product</span><span class="params">(String productQualifier)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.productQualifier = productQualifier;</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">getProductQualifier</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> productQualifier;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>第二步:定义Component</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午8:59.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Component</span> <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserComponent</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// unused</span></span><br><span class="line">  <span class="comment">//void inject(MainActivity mainActivity);</span></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">Initializer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Initializer</span><span class="params">()</span> </span>&#123;</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> UserComponent <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> DaggerUserComponent.create();</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>
<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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午10:00.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@ActivityScope</span> <span class="meta">@Component</span>(dependencies = UserComponent.class,</span><br><span class="line">    modules = ProductModule.class) <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ProductComponent</span> <span class="keyword">extends</span> <span class="title">UserComponent</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity mainActivity)</span></span>;</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">Initializer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Initializer</span><span class="params">()</span> </span>&#123;</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> ProductComponent <span class="title">init</span><span class="params">(UserComponent userComponent)</span> </span>&#123;</span><br><span class="line">      <span class="comment">//dependency usercomponent</span></span><br><span class="line">      <span class="keyword">return</span> DaggerProductComponent.builder().productModule(<span class="keyword">new</span> ProductModule()).userComponent(userComponent).build();</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><strong>注意:</strong> 此处依赖UserComponent,需要将UserComponent中的注入删除.</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="comment">//MainActivity</span></span><br><span class="line"><span class="comment">//inject dependencies</span></span><br><span class="line">ProductComponent.Initializer.init(UserComponent.Initializer.init()).inject(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure>
<p><code>@Scope</code>:</p>
<p>注解作用域.用于更好的组织Component,和定义Component的粒度.<br><code>@Singleton</code>是Dagger2默认实现的,用于管理全局单例(AppComponent中),<br><code>@Scope</code>用在Component 和 Module 头上,<br>如果要定义一个实例的生命周期在Activity内,则可以定义<code>@ActivityScope</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:55.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Scope</span> <span class="meta">@Retention</span>(RetentionPolicy.RUNTIME) <span class="keyword">public</span> <span class="meta">@interface</span> ActivityScope &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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">/**</span><br><span class="line"> * Created on 16/6/8.下午10:00.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@ActivityScope</span> <span class="meta">@Component</span>(dependencies = UserComponent.class,</span><br><span class="line">    modules = ProductModule.class) <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ProductComponent</span> <span class="keyword">extends</span> <span class="title">UserComponent</span> </span>&#123;</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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><br><span class="line"> * Created on 16/6/8.下午9:49.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Module</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProductModule</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@ActivityScope</span> <span class="meta">@Provides</span> <span class="function">Product <span class="title">provideProduct</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Product(<span class="string">"this is a product"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="依赖迷失之Qualifier"><a href="#依赖迷失之Qualifier" class="headerlink" title="依赖迷失之Qualifier"></a>依赖迷失之Qualifier</h2><p><code>@Qualifier</code>:</p>
<p>限定符,当依赖类中需要被依赖类的两个不同对象的时候,帮助我们去为相同接口的依赖创建“tags”.<br>如我们需要两个不同的level的product,Qualifier会帮助你区分对应的一个,</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="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:57.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Qualifier</span> <span class="meta">@Retention</span>(RetentionPolicy.RUNTIME) <span class="keyword">public</span> <span class="meta">@interface</span> ProductLevel &#123;</span><br><span class="line">  <span class="function">String <span class="title">value</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>声明Module</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * Created on 16/6/8.下午9:49.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> bobomee</span><br><span class="line"> */</span></span><br><span class="line"><span class="meta">@Module</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProductModule</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">@ActivityScope</span> <span class="meta">@ProductLevel</span>(<span class="string">"good"</span>) <span class="meta">@Provides</span> <span class="function">Product <span class="title">provideGoodProduct</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Product(<span class="string">"good product"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@ActivityScope</span> <span class="meta">@ProductLevel</span>(<span class="string">"bad"</span>) <span class="meta">@Provides</span> <span class="function">Product <span class="title">provideBadProduct</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Product(<span class="string">"bad product"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>注入Inject</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="meta">@Inject</span> <span class="meta">@ProductLevel</span>(<span class="string">"good"</span>) Product product1;</span><br><span class="line"><span class="meta">@Inject</span> <span class="meta">@ProductLevel</span>(<span class="string">"bad"</span>) Product product2;</span><br></pre></td></tr></table></figure>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul>
<li>Inject用来标注<code>依赖</code>和<code>被依赖的构造函数</code></li>
<li>Provides提供依赖的方法上添加的注解,provide方法需要包含在Module中</li>
<li>Module专门提供依赖,类似工厂模式,包含Provides方法</li>
<li>Component<code>依赖</code>和<code>被依赖</code>的桥梁,(先从Module中找依赖,再从Inject构造函数找)</li>
<li>Scope自定义注解,用于标示作用域,命名随意,对应即可,其中@Singleton是一个系统的模式实现.(管理Module与Component的匹配)</li>
<li>Qualifier自定义注解,用于解决一个实例可以被多种方式构建的依赖迷失问题</li>
<li>Component有三种组织关系,分为依赖,包含和继承,用于解决依赖复用与共享问题</li>
</ul>
<blockquote>
<p>依赖注入的过程:<br>步骤1：查找Module中是否存在创建该类的方法。</p>
<p>步骤2：若存在创建类方法，查看该方法是否存在参数</p>
<p>………..步骤2.1：若存在参数，则按从<strong>步骤1</strong>开始依次初始化每个参数</p>
<p>………..步骤2.2：若不存在参数，则直接初始化该类实例，一次依赖注入到此结束</p>
<p>步骤3：若不存在创建类方法，则查找Inject注解的构造函数，看构造函数是否存在参数</p>
<p>………..步骤3.1：若存在参数，则从<strong>步骤1</strong>开始依次初始化每个参数</p>
<p>………..步骤3.2：若不存在参数，则直接初始化该类实例，一次依赖注入到此结束</p>
</blockquote>
<p>参考:</p>
<p>这里推荐牛晓伟的三篇博客:</p>
<p><a href="http://www.jianshu.com/p/cd2c1c9f68d4" target="_blank" rel="external">Android：dagger2让你爱不释手-基础依赖注入框架篇</a></p>
<p><a href="http://www.jianshu.com/p/1d42d2e6f4a5" target="_blank" rel="external">Android：dagger2让你爱不释手-重点概念讲解、融合篇</a></p>
<p><a href="http://www.jianshu.com/p/65737ac39c44" target="_blank" rel="external">Android：dagger2让你爱不释手-终结篇</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;Dagger2是一款使用在Java和Android上的静态的,运行时依赖注入框架.官方地址:&lt;a href=&quot;http://google.github.io/dagger/&quot;&gt;http://google.github.io/dagger/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;记得当初刚学习Dagger2的时候看了许多博客,但是感觉上手依然困难,所谓光学不练就是这个意思吧&lt;/p&gt;
&lt;p&gt;时至今日,用上此框架的同仁越来越多.分析文章也很多,上手相对要简单了许多.&lt;/p&gt;
&lt;p&gt;学习Dagger2最先要明白的是其各个注解的含义及工作原理,这样才可以快速的上手和使用.&lt;/p&gt;
&lt;p&gt;在这里简要记录一下在使用Dagger2过程中的感受和心得体会.&lt;/p&gt;
&lt;p&gt;本文示例代码地址:&lt;a href=&quot;https://github.com/BoBoMEe/AndroidDev/tree/dagger2/Dagger2Sample&quot;&gt;Dagger2Sample&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="dagger2" scheme="http://bobomee.github.io/tags/dagger2/"/>
    
  </entry>
  
  <entry>
    <title>使用观察者模式完美解决activity与fragment通信问题</title>
    <link href="http://bobomee.github.io/2016/06/05/activityfragment/"/>
    <id>http://bobomee.github.io/2016/06/05/activityfragment/</id>
    <published>2016-06-05T01:07:46.000Z</published>
    <updated>2017-05-14T11:09:13.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>通常来说，解决activity和fragment通信的问题。我们都是采用如下几种方式，<br>handler，广播，EvnetBus，接口等。那么有没有一种更好的方式来实现呢，最近项目一只在使用Rx，这里参照了一下Rx的设计来解决如上问题。先来说说常用的几种方式。</p>
<a id="more"></a>
<h2 id="handler方式"><a href="#handler方式" class="headerlink" title="handler方式"></a>handler方式</h2><p>这是一种最不好的初级方式，在fragment中调用activity中的handler发送信息，activity处理信息。缺点也十分明显，耦合性极强，并且获取不了activity的反馈，总之体验十分不好。</p>
<h2 id="广播"><a href="#广播" class="headerlink" title="广播"></a>广播</h2><p>这是一种另类的玩法，个人感觉大材小用了。广播适用于一对多且接收者不明确的情况下，<br>而且广播传播的信息需要实现序列化借口，总之是一种耗费性能的方式。</p>
<h2 id="EvnetBus"><a href="#EvnetBus" class="headerlink" title="EvnetBus"></a>EvnetBus</h2><p>EvnetBus作为一款事件总线存在，可以用于Action的传递，但是这种方式的缺点有：是用反射（对性能有影响），EvnetBus发送者和接受者分离严重，不好维护，且不可以获取activity反馈，也不是十分理想。</p>
<h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p>之前处理这类问题一直也是适应这种方式，总体来讲还是很不错。但是也有缺点，当项目大了的时候，就需要定义很多的接口，这时候就容易混淆，不好维护。</p>
<h2 id="新大陆"><a href="#新大陆" class="headerlink" title="新大陆"></a>新大陆</h2><p>总结一下Rx的设计思路，Rx和设计模式中的观察者十分类似。activity和fragment通信，实质上就是所谓事件的传递和响应，只是这个事件是一个特殊的事件，就是可以执行的方法（js中function也可以是参数），那么如果activity需要监听fragment的事件，只需要观察其相应事件即可，fragment同理。</p>
<p>如果对观察者不熟悉的可以看一下：<a href="http://droidyue.com/blog/2015/06/27/desgign-pattern-observer/" target="_blank" rel="external">这就是观察者模式</a></p>
<h3 id="Observable"><a href="#Observable" class="headerlink" title="Observable"></a>Observable</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></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">ObservableInterface</span>&lt;<span class="title">T</span>, <span class="title">P</span>, <span class="title">R</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 根据名称注册观察者</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">registerObserver</span><span class="params">(String name, T observer)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 根据名称反注册观察者</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">removeObserver</span><span class="params">(String name)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 根据观察者反注册</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">removeObserver</span><span class="params">(T observer)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 根据名称和观察者反注册</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">removeObserver</span><span class="params">(String name, T observer)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 根据名称获取观察者</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function">Set&lt;T&gt; <span class="title">getObserver</span><span class="params">(String name)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 清除观察者</span><br><span class="line">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span><br><span class="line">   * 通知观察者</span><br><span class="line">   *</span><br><span class="line">   * <span class="doctag">@param</span> name 名称</span><br><span class="line">   * <span class="doctag">@param</span> p 参数</span><br><span class="line">   * <span class="doctag">@return</span> 返回值</span><br><span class="line">   */</span></span><br><span class="line"></span><br><span class="line">  <span class="function">R <span class="title">notify</span><span class="params">(String name, P... p)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="Observer"><a href="#Observer" class="headerlink" title="Observer"></a>Observer</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></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">Function</span>&lt;<span class="title">Result</span>, <span class="title">Param</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function">Result <span class="title">function</span><span class="params">(Param... data)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里的Observer就是一个方法的抽象，包含 参数 和 返回值。在Activity和Fragment中传递的就是Fuction，在需要监听的地方注册事件，在适当的地方触发事件。下面看一下Observable的具体实现</p>
<h3 id="ObservableManager"><a href="#ObservableManager" class="headerlink" title="ObservableManager"></a>ObservableManager</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></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">ObservableManager</span>&lt;<span class="title">Param</span>, <span class="title">Result</span>&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title">ObservableInterface</span>&lt;<span class="title">Function</span>, <span class="title">Param</span>, <span class="title">Result</span>&gt; </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">"ObservableManager"</span>;</span><br><span class="line">  <span class="keyword">private</span> HashMap&lt;String, Set&lt;Function&gt;&gt; _mapping;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Object _lockObj = <span class="keyword">new</span> Object();</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> ObservableManager _instance;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">ObservableManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>._mapping = <span class="keyword">new</span> HashMap&lt;&gt;();</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> ObservableManager <span class="title">newInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (_instance == <span class="keyword">null</span>) _instance = <span class="keyword">new</span> ObservableManager();</span><br><span class="line">    <span class="keyword">return</span> _instance;</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">registerObserver</span><span class="params">(String name, Function observer)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      Set&lt;Function&gt; observers;</span><br><span class="line">      <span class="keyword">if</span> (!_mapping.containsKey(name)) &#123;</span><br><span class="line">        observers = <span class="keyword">new</span> HashSet&lt;&gt;();</span><br><span class="line">        _mapping.put(name, observers);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        observers = _mapping.get(name);</span><br><span class="line">      &#125;</span><br><span class="line">      observers.add(observer);</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">removeObserver</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      _mapping.remove(name);</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">removeObserver</span><span class="params">(Function observer)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      <span class="keyword">for</span> (String key : _mapping.keySet()) &#123;</span><br><span class="line">        Set&lt;Function&gt; observers = _mapping.get(key);</span><br><span class="line">        observers.remove(observer);</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">removeObserver</span><span class="params">(String name, Function observer)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      <span class="keyword">if</span> (_mapping.containsKey(name)) &#123;</span><br><span class="line">        Set&lt;Function&gt; observers = _mapping.get(name);</span><br><span class="line">        observers.remove(observer);</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> Set&lt;Function&gt; <span class="title">getObserver</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">    Set&lt;Function&gt; observers = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      <span class="keyword">if</span> (_mapping.containsKey(name)) &#123;</span><br><span class="line">        observers = _mapping.get(name);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> observers;</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">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      _mapping.clear();</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> Result <span class="title">notify</span><span class="params">(String name, Param... param)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (_lockObj) &#123;</span><br><span class="line">      <span class="keyword">if</span> (_mapping.containsKey(name)) &#123;</span><br><span class="line">        Set&lt;Function&gt; observers = _mapping.get(name);</span><br><span class="line">        <span class="keyword">for</span> (Function o : observers) &#123;</span><br><span class="line">          <span class="keyword">return</span> (Result) o.function(param);</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">null</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>这里才用了一个HashMap来存储所有Observer集合，触发事件后就轮询集合循序之行，这就是观察者其中之一的缺点，一个Observer可能阻塞，影响其他执行。</p>
<h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><p>Activity中注册Function事件，Fragment中出发Function事件，执行的到结果，反之亦然。</p>
<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">// 注册事件,Activity实现Function接口</span></span><br><span class="line">ObservableManager.newInstance().registerObserver(FUNCTION_WITH_PARAM_AND_RESULT, <span class="keyword">this</span>);</span><br></pre></td></tr></table></figure>
<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">//触发事件，并获得返回值</span></span><br><span class="line">Object notify = ObservableManager.newInstance()</span><br><span class="line">        .notify(MainActivity.FUNCTION_WITH_PARAM_AND_RESULT, <span class="string">"我是fragment传到activity的参数1"</span>, fragmentActivity,</span><br><span class="line">            fragmentResult);</span><br></pre></td></tr></table></figure>
<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="comment">//事件执行，实现于Function接口</span></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Object <span class="title">function</span><span class="params">(Object[] data)</span> </span>&#123;</span><br><span class="line">   String text = activityResult.getText().toString() + <span class="string">"\n\n\n"</span>;</span><br><span class="line"></span><br><span class="line">   activityResult.setText(text + <span class="string">"fragment调用我成功,获取到参数 :"</span> + Arrays.asList(data));</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> <span class="string">"我是activity的返回结果"</span>;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>最终效果:</p>
<p><img src="http://img.blog.csdn.net/20160612215028767" alt="activityfragment"></p>
<p>示例代码：<a href="https://github.com/BoBoMEe/AndroidDev/tree/activityfragment/activityfragment" target="_blank" rel="external">ActivityFragment</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;通常来说，解决activity和fragment通信的问题。我们都是采用如下几种方式，&lt;br&gt;handler，广播，EvnetBus，接口等。那么有没有一种更好的方式来实现呢，最近项目一只在使用Rx，这里参照了一下Rx的设计来解决如上问题。先来说说常用的几种方式。&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="activity" scheme="http://bobomee.github.io/tags/activity/"/>
    
      <category term="fragment" scheme="http://bobomee.github.io/tags/fragment/"/>
    
  </entry>
  
  <entry>
    <title>Android无限轮播Banner的实现</title>
    <link href="http://bobomee.github.io/2016/05/12/androidbanner/"/>
    <id>http://bobomee.github.io/2016/05/12/androidbanner/</id>
    <published>2016-05-12T12:02:00.000Z</published>
    <updated>2017-05-14T11:05:23.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><hr>
<p>应用首页的广告轮播<code>Banner</code>，一般都会使用<code>ViewPager</code>来实现，但是<code>ViewPager</code> 没有轮播效果。<br>现成有这么几种实现方案:</p>
<p>1.使用<code>Integer.MAX_VALUE</code> ,理论上很难达到边界。<br><br>2.装饰<code>adapter</code>方式<a href="https://github.com/imbryk/LoopingViewPager" target="_blank" rel="external">imbryk/LoopingViewPager@Github</a>。<br><br>3.扩展<code>ViewPager</code>方式<a href="https://github.com/yanzm/LoopViewPager" target="_blank" rel="external">yanzm/LoopViewPager</a>。</p>
<a id="more"></a>
<h2 id="相关库"><a href="#相关库" class="headerlink" title="相关库"></a>相关库</h2><hr>
<p>轮播效果其实就是一个定时任务，可以用定时器，<code>Handler</code>等。</p>
<p><a href="https://github.com/Trinea/android-auto-scroll-view-pager" target="_blank" rel="external">Trinea/Android Auto Scroll ViewPager@Github</a></p>
<p>此库通过<code>handler</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><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="comment">/**</span><br><span class="line"> * scroll only once</span><br><span class="line"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scrollOnce</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    PagerAdapter adapter = getAdapter();</span><br><span class="line">    <span class="keyword">int</span> currentItem = getCurrentItem();</span><br><span class="line">    <span class="keyword">int</span> totalCount;</span><br><span class="line">    <span class="keyword">if</span> (adapter == <span class="keyword">null</span> || (totalCount = adapter.getCount()) &lt;= <span class="number">1</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">int</span> nextItem = (direction == LEFT) ? --currentItem : ++currentItem;</span><br><span class="line">    <span class="keyword">if</span> (nextItem &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isCycle) &#123;</span><br><span class="line">            setCurrentItem(totalCount - <span class="number">1</span>, isBorderAnimation);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nextItem == totalCount) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isCycle) &#123;</span><br><span class="line">            setCurrentItem(<span class="number">0</span>, isBorderAnimation);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        setCurrentItem(nextItem, <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>这里是在最后或第一页直接<code>setCurrentItem()</code>,因此，并非无缝轮播效果。</p>
<p>和本文开头的几个控件整合一下，即可方便 的实现我们想要无缝 的效果。</p>
<h2 id="基本实现"><a href="#基本实现" class="headerlink" title="基本实现"></a>基本实现</h2><hr>
<h3 id="1-修改Trinea-Android-Auto-Scroll-ViewPager-Github"><a href="#1-修改Trinea-Android-Auto-Scroll-ViewPager-Github" class="headerlink" title="1.修改Trinea/Android Auto Scroll ViewPager@Github"></a>1.修改<a href="https://github.com/Trinea/android-auto-scroll-view-pager" target="_blank" rel="external">Trinea/Android Auto Scroll ViewPager@Github</a></h3><p>改变首页和最后一页的跳转设置。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * scroll only once</span><br><span class="line"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scrollOnce</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    PagerAdapter adapter = getAdapter();</span><br><span class="line">    <span class="keyword">int</span> currentItem = getCurrentItem();</span><br><span class="line">    <span class="keyword">if</span> (adapter == <span class="keyword">null</span> || adapter.getCount() &lt;= <span class="number">1</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">int</span> nextItem = (direction == LEFT) ? --currentItem : ++currentItem;</span><br><span class="line">    setCurrentItem(nextItem, <span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="2-实现方式选择"><a href="#2-实现方式选择" class="headerlink" title="2.实现方式选择"></a>2.实现方式选择</h3><p>第一种方式其实主要改变了<code>adapter</code>中的<code>index</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></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">int</span> <span class="title">getCount</span><span class="params">()</span> </span>&#123;  </span><br><span class="line">  <span class="keyword">return</span> Integer.MAX_VALUE;  </span><br><span class="line">&#125;</span><br><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">instantiateItem</span><span class="params">(ViewGroup container, <span class="keyword">int</span> position)</span> </span>&#123;  </span><br><span class="line">  position = position % listviews.size()  ;</span><br><span class="line">  <span class="comment">//....       </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>只是理论上的无限，需要自己处理<code>position</code>。</p>
<p>第二种装饰了<code>adapter</code>，在前后各添加一个<code>Item</code>，当到了我们添加的临界<code>item</code>的时候，立即设置到正确的 <code>position</code> ，主要方法如下：</p>
<p><code>LoopPagerAdapterWrapper</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">toRealPosition</span><span class="params">(<span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> realCount = getRealCount();</span><br><span class="line">        <span class="keyword">if</span> (realCount == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> realPosition = (position-<span class="number">1</span>) % realCount;</span><br><span class="line">        <span class="keyword">if</span> (realPosition &lt; <span class="number">0</span>)</span><br><span class="line">            realPosition += realCount;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> realPosition;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p>
<p>这里，内部封装<code>position</code>映射，不需要自己处理<code>position</code>，和正常<code>ViewPager</code>使用方式一样。</p>
<p>第三种，重写扩展<code>ViewPager</code> 继承 <code>ViewGroup</code>，使其<code>item</code>无限。需要自己处理<code>position</code>，而且 和 <a href="https://github.com/JakeWharton/ViewPagerIndicator" target="_blank" rel="external">JakeWharton/ViewPagerIndicator@Github</a>搭配使用起来不是很方便，<code>setViewPager</code>方法需要额外处理。</p>
<p>综上，使用第二种方式 扩展性和 易用性都比较好。</p>
<h2 id="Indicator"><a href="#Indicator" class="headerlink" title="Indicator"></a>Indicator</h2><hr>
<p><a href="https://github.com/JakeWharton/ViewPagerIndicator" target="_blank" rel="external">JakeWharton/ViewPagerIndicator@Github</a><br>这是一个比较全面的<code>indicator</code>，如果我们只需要简单的<code>CircleIndicator</code>，可以抽取来使用。</p>
<p>优秀相关开源库：</p>
<p><a href="https://github.com/THEONE10211024/CircleIndicator" target="_blank" rel="external">THEONE10211024/CircleIndicator@Github</a>:一个轻量的圆形指示器</p>
<p><a href="https://github.com/ongakuer/CircleIndicator" target="_blank" rel="external">ongakuer/CircleIndicator@Github</a>：才用Drawable写的圆形指示器</p>
<p>也可以看看本人站在巨人肩膀上完成的<a href="https://github.com/BoBoMEe/DrawableIndicator" target="_blank" rel="external">DrawableIndicator@Github</a>：可以实时偏移，切换动画，drawable资源。</p>
<p>效果图：</p>
<p><img src="https://github.com/BoBoMEe/DrawableIndicator/raw/master/gif.gif" alt="DrawableIndicator"></p>
<h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><hr>
<p>my_banner.xml：</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><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">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">RelativeLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    <span class="attr">xmlns:app</span>=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span><br><span class="line">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span><br><span class="line">    <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">com.bobomee.android.scrollloopviewpager.autoscrollviewpager.AutoScrollViewPager</span></span><br><span class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/picslooper1"</span></span><br><span class="line">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span><br><span class="line">        <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">com.bobomee.android.drawableindicator.widget.DrawableIndicator</span></span><br><span class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/pageIndexor1"</span></span><br><span class="line">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span><br><span class="line">        <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span><br><span class="line">        <span class="attr">android:layout_centerVertical</span>=<span class="string">"true"</span></span><br><span class="line">        <span class="attr">app:indicator_height</span>=<span class="string">"15dp"</span></span><br><span class="line">        <span class="attr">app:indicator_margin</span>=<span class="string">"15dp"</span></span><br><span class="line">        <span class="attr">app:indicator_select_src</span>=<span class="string">"@drawable/select_drawable"</span></span><br><span class="line">        <span class="attr">app:indicator_unselect_src</span>=<span class="string">"@drawable/unselect_drawable"</span></span><br><span class="line">        <span class="attr">app:indicator_width</span>=<span class="string">"15dp"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">RelativeLayout</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p> MainActivity:</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">AutoScrollViewPager viewPager = (AutoScrollViewPager) findViewById(R.id.picslooper1);</span><br><span class="line">      viewPager.setFocusable(<span class="keyword">true</span>);</span><br><span class="line">      viewPager.setAdapter(<span class="keyword">new</span> FragmentStateAdapter(getSupportFragmentManager()));</span><br><span class="line"></span><br><span class="line">      BaseIndicator pageIndex = (BaseIndicator) findViewById(R.id.pageIndexor1);</span><br><span class="line">      pageIndex.setViewPager(viewPager);</span><br><span class="line"></span><br><span class="line">      viewPager.startAutoScroll();</span><br></pre></td></tr></table></figure>
<p>最终效果图<br><img src="https://github.com/BoBoMEe/AutoScrollLoopViewPager/raw/master/screenshot/shot.gif" alt="AutoScrollLoopViewPager"></p>
<p>源码:<br><a href="https://github.com/BoBoMEe/AutoScrollLoopViewPager" target="_blank" rel="external">AutoScrollLoopViewPager@Github</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;hr&gt;
&lt;p&gt;应用首页的广告轮播&lt;code&gt;Banner&lt;/code&gt;，一般都会使用&lt;code&gt;ViewPager&lt;/code&gt;来实现，但是&lt;code&gt;ViewPager&lt;/code&gt; 没有轮播效果。&lt;br&gt;现成有这么几种实现方案:&lt;/p&gt;
&lt;p&gt;1.使用&lt;code&gt;Integer.MAX_VALUE&lt;/code&gt; ,理论上很难达到边界。&lt;br/&gt;&lt;br&gt;2.装饰&lt;code&gt;adapter&lt;/code&gt;方式&lt;a href=&quot;https://github.com/imbryk/LoopingViewPager&quot;&gt;imbryk/LoopingViewPager@Github&lt;/a&gt;。&lt;br/&gt;&lt;br&gt;3.扩展&lt;code&gt;ViewPager&lt;/code&gt;方式&lt;a href=&quot;https://github.com/yanzm/LoopViewPager&quot;&gt;yanzm/LoopViewPager&lt;/a&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="自定义控件" scheme="http://bobomee.github.io/categories/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6/"/>
    
    
      <category term="banner" scheme="http://bobomee.github.io/tags/banner/"/>
    
  </entry>
  
  <entry>
    <title>Retrofit2.0使用总结及注意事项</title>
    <link href="http://bobomee.github.io/2016/05/12/retrofit/"/>
    <id>http://bobomee.github.io/2016/05/12/retrofit/</id>
    <published>2016-05-11T16:13:46.000Z</published>
    <updated>2017-05-14T11:11:41.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>随着Google对HttpClient 摒弃,和Volley的逐渐没落,OkHttp开始异军突起,而Retrofit则对okHttp进行了强制依赖。</p>
<p>Retrofit是由<a href="https://github.com/square" target="_blank" rel="external">Square</a>公司出品的针对于Android和Java的类型安全的Http客户端，</p>
<p>如果看源码会发现其实质上就是对okHttp的封装，使用面向接口的方式进行网络请求，利用动态生成的代理类封装了网络接口请求的底层,</p>
<p>其将请求返回javaBean，对网络认证 REST API进行了很好对支持此，使用Retrofit将会极大的提高我们应用的网络体验。</p>
<a id="more"></a>
<h2 id="REST"><a href="#REST" class="headerlink" title="REST"></a>REST</h2><p>既然是RESTful架构,那么我们就来看一下什么是REST吧。<br>REST(REpresentational State Transfer)是一组架构约束条件和原则。<br>RESTful架构都满足以下规则：<br>（1）每一个URI代表一种资源；<br>（2）客户端和服务器之间，传递这种资源的某种表现层；<br>（3）客户端通过四个HTTP动词，对服务器端资源进行操作，实现”表现层状态转化”。</p>
<p>更多关于REST的介绍：<a href="https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.3.md" target="_blank" rel="external">什么是REST - GitHub</a>讲解的非常详细</p>
<h2 id="2-0与1-9使用比较"><a href="#2-0与1-9使用比较" class="headerlink" title="2.0与1.9使用比较"></a>2.0与1.9使用比较</h2><p>如果之前使用过Retrofit1，会发现2.0后的API会有一些变化，<br>比如创建方式，拦截器，错误处理，转换器等，如下我们列举以下他们使用起来具体的区别有哪些。</p>
<p>(1)  在Retrofit1中使用的是RestAdapter，而Retrofit2中使用的Retrofit实例，之前的setEndpoint变为了baseUrl。<br>(2)  Retrofit1中使用setRequestInterceptor设置拦截器，对http请求进行相应等处理。<br>(3)  Retrofit2通过OKHttp的拦截器拦截http请求进行监控，重写或重试等，包括日志打印等。<br>(4)  converter，Retrofit1中的setConverter，换以addConverterFactory，用于支持Gson转换。</p>
<p>Retrofit1体验不好的地方:</p>
<p>(1)  Retrofit1不能同时操作response返回数据<code>(比如说返回的 Header 部分或者 URL)</code>和序列化后的数据<code>(JAVABEAN)</code>，<br>(2)  Retrofit1中同步和异步执行同一个方法需要分别定义接口。<br>(3)  Retrofit1对正在进行的网络任务无法取消。</p>
<p>参考：<a href="https://github.com/square/retrofit/blob/master/CHANGELOG.md" target="_blank" rel="external">官方CHANGELOG.md</a><br><a href="http://blog.csdn.net/tiankong1206/article/details/50720758" target="_blank" rel="external">更新到Retrofit2的一些技巧</a></p>
<h2 id="1-9使用配置："><a href="#1-9使用配置：" class="headerlink" title="1.9使用配置："></a>1.9使用配置：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//gson converter</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">static</span> Gson gson = <span class="keyword">new</span> GsonBuilder()</span><br><span class="line">    .setDateFormat(<span class="string">"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"</span>)</span><br><span class="line">    .serializeNulls()</span><br><span class="line">    .create();</span><br><span class="line"></span><br><span class="line"><span class="comment">// client</span></span><br><span class="line">OkHttpClient client = <span class="keyword">new</span> OkHttpClient();</span><br><span class="line">client.setReadTimeout(<span class="number">12</span>, TimeUnit.SECONDS);</span><br><span class="line"></span><br><span class="line">RestAdapter.Builder builder = <span class="keyword">new</span> RestAdapter.Builder();</span><br><span class="line">builder.setClient(<span class="keyword">new</span> OkClient(client))</span><br><span class="line">       <span class="comment">//日志打印</span></span><br><span class="line">       .setLogLevel(RestAdapter.LogLevel.FULL)</span><br><span class="line">       <span class="comment">//baseUrl</span></span><br><span class="line">       .setEndpoint(<span class="string">"https://api.github.com"</span>)</span><br><span class="line">       <span class="comment">//转换器</span></span><br><span class="line">       .setConverter(<span class="keyword">new</span> GsonConverter(gson))</span><br><span class="line">       <span class="comment">//错误处理</span></span><br><span class="line">       .setErrorHandler(<span class="keyword">new</span> ErrorHandler() &#123;</span><br><span class="line">                   <span class="meta">@Override</span></span><br><span class="line">                   <span class="function"><span class="keyword">public</span> Throwable <span class="title">handleError</span><span class="params">(RetrofitError cause)</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><span class="line">               &#125;)</span><br><span class="line">       <span class="comment">//拦截器</span></span><br><span class="line">       .setRequestInterceptor(authorizationInterceptor)</span><br><span class="line">RestAdapter restAdapter = builder.build();</span><br><span class="line">apiService = restAdapter.create(ApiService.class);</span><br></pre></td></tr></table></figure>
<h2 id="2-0使用配置"><a href="#2-0使用配置" class="headerlink" title="2.0使用配置"></a>2.0使用配置</h2><p>引入依赖</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></pre></td><td class="code"><pre><span class="line">compile <span class="string">'com.squareup.retrofit2:retrofit:2.0.2'</span></span><br><span class="line">compile <span class="string">'com.squareup.retrofit2:converter-gson:2.0.2'</span></span><br><span class="line">compile <span class="string">'com.squareup.retrofit2:adapter-rxjava:2.0.2'</span></span><br><span class="line"></span><br><span class="line">compile <span class="string">'com.squareup.okhttp3:logging-interceptor:3.2.0'</span></span><br></pre></td></tr></table></figure>
<p>OkHttp配置</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">HttpLoggingInterceptor interceptor = <span class="keyword">new</span> HttpLoggingInterceptor();</span><br><span class="line">     interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);</span><br><span class="line">     client = <span class="keyword">new</span> OkHttpClient.Builder()</span><br><span class="line">             .addInterceptor(interceptor)</span><br><span class="line">             .retryOnConnectionFailure(<span class="keyword">true</span>)</span><br><span class="line">             .connectTimeout(<span class="number">15</span>, TimeUnit.SECONDS)</span><br><span class="line">             .addNetworkInterceptor(authorizationInterceptor)</span><br><span class="line">             .build();</span><br></pre></td></tr></table></figure>
<p>其中 level 为 BASIC / HEADERS / BODY，BODY等同于1.9中的FULL<br>retryOnConnectionFailure:错误重联<br>addInterceptor:设置应用拦截器，可用于设置公共参数，头信息，日志拦截等<br>addNetworkInterceptor：网络拦截器，可以用于重试或重写，对应与1.9中的setRequestInterceptor。<br>参考：<a href="https://github.com/square/okhttp/wiki/Interceptors" target="_blank" rel="external">Interceptors</a><br>中文翻译：<a href="http://www.jianshu.com/p/2710ed1e6b48" target="_blank" rel="external">Okhttp-wiki 之 Interceptors 拦截器</a></p>
<p>Retrofit配置</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></pre></td><td class="code"><pre><span class="line">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">.baseUrl(BASE_URL)</span><br><span class="line">.client(client)</span><br><span class="line">.addCallAdapterFactory(RxJavaCallAdapterFactory.create())</span><br><span class="line">.addConverterFactory(GsonConverterFactory.create(gson))</span><br><span class="line">.build();</span><br><span class="line">apiService = retrofit.create(ApiService.class);</span><br></pre></td></tr></table></figure>
<p>其中baseUrl相当于1.9中的setEndPoint,<br>addCallAdapterFactory提供RxJava支持，如果没有提供响应的支持(RxJava,Call),则会跑出异常。<br>addConverterFactory提供Gson支持，可以添加多种序列化Factory，但是GsonConverterFactory必须放在最后,否则会抛出异常。<br>参考：<a href="https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/" target="_blank" rel="external">用 Retrofit 2 简化 HTTP 请求</a></p>
<h2 id="2-0使用介绍"><a href="#2-0使用介绍" class="headerlink" title="2.0使用介绍"></a>2.0使用介绍</h2><p><font color="#DC143C" size="5">注意：</font>retrofit2.0后：BaseUrl要以/结尾；@GET 等请求不要以/开头；@Url: 可以定义完整url，不要以 / 开头。<br>关于URL拼接注意事项：<a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html" target="_blank" rel="external">Retrofit 2.0：有史以来最大的改进</a></p>
<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><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="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GitHubService</span> </span>&#123;</span><br><span class="line">  <span class="meta">@GET</span>(<span class="string">"users/&#123;user&#125;/repos"</span>)</span><br><span class="line">  Call&lt;List&lt;Repo&gt;&gt; listRepos(<span class="meta">@Path</span>(<span class="string">"user"</span>) String user);</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">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">	<span class="comment">//设置OKHttpClient,如果不设置会提供一个默认的</span></span><br><span class="line">	.client(<span class="keyword">new</span> OkHttpClient())</span><br><span class="line">	<span class="comment">//设置baseUrl</span></span><br><span class="line">    .baseUrl(<span class="string">"https://api.github.com/"</span>)</span><br><span class="line">    <span class="comment">//添加Gson转换器</span></span><br><span class="line">    .addConverterFactory(GsonConverterFactory.create())</span><br><span class="line">    .build();</span><br><span class="line"></span><br><span class="line">GitHubService service = retrofit.create(GitHubService.class);</span><br><span class="line"></span><br><span class="line"><span class="comment">//同步请求</span></span><br><span class="line"><span class="comment">//https://api.github.com/users/octocat/repos</span></span><br><span class="line">Call&lt;List&lt;Repo&gt;&gt; call = service.listRepos(<span class="string">"octocat"</span>);</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">     Response&lt;List&lt;Repo&gt;&gt; repos  = call.execute();</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"></span><br><span class="line"><span class="comment">//call只能调用一次。否则会抛 IllegalStateException</span></span><br><span class="line">Call&lt;List&lt;Repo&gt;&gt; clone = call.clone();</span><br><span class="line"></span><br><span class="line"><span class="comment">//异步请求</span></span><br><span class="line">clone.enqueue(<span class="keyword">new</span> Callback&lt;List&lt;Repo&gt;&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">(Call&lt;List&lt;Repo&gt;&gt; call, Response&lt;List&lt;Repo&gt;&gt; response)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Get result bean from response.body()</span></span><br><span class="line">            List&lt;Repo&gt; repos = response.body();</span><br><span class="line">            <span class="comment">// Get header item from response</span></span><br><span class="line">            String links = response.headers().get(<span class="string">"Link"</span>);</span><br><span class="line">            <span class="comment">/**</span><br><span class="line">			  * 不同于retrofit1 可以同时操作序列化数据javabean和header</span><br><span class="line">			  */</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">onFailure</span><span class="params">(Call&lt;List&lt;Repo&gt;&gt; call, Throwable t)</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"><span class="comment">// 取消</span></span><br><span class="line">call.cancel();</span><br></pre></td></tr></table></figure>
<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">//rxjava support</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GitHubService</span> </span>&#123;</span><br><span class="line">  <span class="meta">@GET</span>(<span class="string">"users/&#123;user&#125;/repos"</span>)</span><br><span class="line">  Observable&lt;List&lt;Repo&gt;&gt; listRepos(<span class="meta">@Path</span>(<span class="string">"user"</span>) String user);</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">// Http request</span></span><br><span class="line">Observable&lt;List&lt;Repo&gt;&gt; call = service.listRepos(<span class="string">"octocat"</span>);</span><br></pre></td></tr></table></figure>
<h3 id="retrofit注解："><a href="#retrofit注解：" class="headerlink" title="retrofit注解："></a>retrofit注解：</h3><p>方法注解，包含@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP。<br>标记注解，包含@FormUrlEncoded、@Multipart、@Streaming。<br>参数注解，包含@Query,@QueryMap、@Body、@Field，@FieldMap、@Part，@PartMap。</p>
<p>其他注解，@Path、@Header,@Headers、@Url</p>
<p><font color="#DC143C" size="5">几个特殊的注解</font><br>@HTTP：可以替代其他方法的任意一种</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line">  * method 表示请的方法，不区分大小写</span><br><span class="line">  * path表示路径</span><br><span class="line">  * hasBody表示是否有请求体</span><br><span class="line">  */</span></span><br><span class="line"> <span class="meta">@HTTP</span>(method = <span class="string">"get"</span>, path = <span class="string">"users/&#123;user&#125;"</span>, hasBody = <span class="keyword">false</span>)</span><br><span class="line"> <span class="function">Call&lt;ResponseBody&gt; <span class="title">getFirstBlog</span><span class="params">(@Path(<span class="string">"user"</span>)</span> String user)</span>;</span><br></pre></td></tr></table></figure>
<p>@Url：使用全路径复写baseUrl，适用于非统一baseUrl的场景。</p>
<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="meta">@GET</span></span><br><span class="line"><span class="function">Call&lt;ResponseBody&gt; <span class="title">v3</span><span class="params">(@Url String url)</span></span>;</span><br></pre></td></tr></table></figure>
<p>@Streaming:用于下载大文件</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="meta">@Streaming</span></span><br><span class="line"><span class="meta">@GET</span></span><br><span class="line"><span class="function">Call&lt;ResponseBody&gt; <span class="title">downloadFileWithDynamicUrlAsync</span><span class="params">(@Url String fileUrl)</span></span>;</span><br></pre></td></tr></table></figure>
<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">ResponseBody body = response.body();</span><br><span class="line"><span class="keyword">long</span> fileSize = body.contentLength();</span><br><span class="line">InputStream inputStream = body.byteStream();</span><br></pre></td></tr></table></figure>
<p><font color="#DC143C" size="5">常用注解</font><br>@Path：URL占位符，用于替换和动态更新,相应的参数必须使用相同的字符串被@Path进行注释</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="meta">@GET</span>(<span class="string">"group/&#123;id&#125;/users"</span>)</span><br><span class="line">Call&lt;List&lt;User&gt;&gt; groupList(<span class="meta">@Path</span>(<span class="string">"id"</span>) <span class="keyword">int</span> groupId);</span><br><span class="line"><span class="comment">//--&gt; http://baseurl/group/groupId/users</span></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><br><span class="line">Call&lt;List&lt;User&gt;&gt; groupListUrl(</span><br><span class="line">      <span class="meta">@Url</span> String url);</span><br></pre></td></tr></table></figure>
<p>@Query,@QueryMap:查询参数，用于GET查询,需要注意的是@QueryMap可以约定是否需要encode</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="meta">@GET</span>(<span class="string">"group/users"</span>)</span><br><span class="line">Call&lt;List&lt;User&gt;&gt; groupList(<span class="meta">@Query</span>(<span class="string">"id"</span>) <span class="keyword">int</span> groupId);</span><br><span class="line"><span class="comment">//--&gt; http://baseurl/group/users?id=groupId</span></span><br></pre></td></tr></table></figure>
<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">Call&lt;List&lt;News&gt;&gt; getNews((@QueryMap(encoded=true) Map&lt;String, String&gt; options);</span><br></pre></td></tr></table></figure>
<p>@Body:用于POST请求体，将实例对象根据转换方式转换为对应的json字符串参数，<br>这个转化方式是GsonConverterFactory定义的。</p>
<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="meta">@POST</span>(<span class="string">"add"</span>)</span><br><span class="line">Call&lt;List&lt;User&gt;&gt; addUser(<span class="meta">@Body</span> User user);</span><br></pre></td></tr></table></figure>
<p>@Field，@FieldMap:Post方式传递简单的键值对,<br>需要添加@FormUrlEncoded表示表单提交<br>Content-Type:application/x-www-form-urlencoded</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="meta">@FormUrlEncoded</span></span><br><span class="line"><span class="meta">@POST</span>(<span class="string">"user/edit"</span>)</span><br><span class="line"><span class="function">Call&lt;User&gt; <span class="title">updateUser</span><span class="params">(@Field(<span class="string">"first_name"</span>)</span> String first, @<span class="title">Field</span><span class="params">(<span class="string">"last_name"</span>)</span> String last)</span>;</span><br></pre></td></tr></table></figure>
<p>@Part，@PartMap：用于POST文件上传<br>其中@Part MultipartBody.Part代表文件，@Part(“key”) RequestBody代表参数<br>需要添加@Multipart表示支持文件上传的表单，Content-Type: multipart/form-data</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Multipart</span></span><br><span class="line">   <span class="meta">@POST</span>(<span class="string">"upload"</span>)</span><br><span class="line">   <span class="function">Call&lt;ResponseBody&gt; <span class="title">upload</span><span class="params">(@Part(<span class="string">"description"</span>)</span> RequestBody description,</span><br><span class="line">                             @Part MultipartBody.Part file)</span>;</span><br></pre></td></tr></table></figure>
<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">// https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java</span></span><br><span class="line"><span class="comment">// use the FileUtils to get the actual file by uri</span></span><br><span class="line">File file = FileUtils.getFile(<span class="keyword">this</span>, fileUri);</span><br><span class="line"></span><br><span class="line"><span class="comment">// create RequestBody instance from file</span></span><br><span class="line">RequestBody requestFile =</span><br><span class="line">        RequestBody.create(MediaType.parse(<span class="string">"multipart/form-data"</span>), file);</span><br><span class="line"></span><br><span class="line"><span class="comment">// MultipartBody.Part is used to send also the actual file name</span></span><br><span class="line">MultipartBody.Part body =</span><br><span class="line">        MultipartBody.Part.createFormData(<span class="string">"picture"</span>, file.getName(), requestFile);</span><br><span class="line"></span><br><span class="line"><span class="comment">// add another part within the multipart request</span></span><br><span class="line">String descriptionString = <span class="string">"hello, this is description speaking"</span>;</span><br><span class="line">RequestBody description =</span><br><span class="line">        RequestBody.create(</span><br><span class="line">                MediaType.parse(<span class="string">"multipart/form-data"</span>), descriptionString);</span><br></pre></td></tr></table></figure>
<p>参考：<a href="http://blog.csdn.net/lmj623565791/article/details/51304204" target="_blank" rel="external"> Retrofit2 完全解析 探索与okhttp之间的关系</a><br><a href="https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server" target="_blank" rel="external">Retrofit 2 — How to Upload Files to Server</a></p>
<p>@Header：header处理，不能被互相覆盖，用于修饰参数，</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="comment">//动态设置Header值</span></span><br><span class="line"><span class="meta">@GET</span>(<span class="string">"user"</span>)</span><br><span class="line"><span class="function">Call&lt;User&gt; <span class="title">getUser</span><span class="params">(@Header(<span class="string">"Authorization"</span>)</span> String authorization)</span></span><br></pre></td></tr></table></figure>
<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></pre></td><td class="code"><pre><span class="line"><span class="comment">//静态设置Header值</span></span><br><span class="line"><span class="meta">@Headers</span>(<span class="string">"Authorization: authorization"</span>)<span class="comment">//这里authorization就是上面方法里传进来变量的值</span></span><br><span class="line"><span class="meta">@GET</span>(<span class="string">"widget/list"</span>)</span><br><span class="line"><span class="function">Call&lt;User&gt; <span class="title">getUser</span><span class="params">()</span></span></span><br></pre></td></tr></table></figure></p>
<p>@Headers 用于修饰方法,用于设置多个Header值：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Headers</span>(&#123;</span><br><span class="line">    <span class="string">"Accept: application/vnd.github.v3.full+json"</span>,</span><br><span class="line">    <span class="string">"User-Agent: Retrofit-Sample-App"</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="meta">@GET</span>(<span class="string">"users/&#123;username&#125;"</span>)</span><br><span class="line"><span class="function">Call&lt;User&gt; <span class="title">getUser</span><span class="params">(@Path(<span class="string">"username"</span>)</span> String username)</span>;</span><br></pre></td></tr></table></figure>
<h2 id="自定义Converter"><a href="#自定义Converter" class="headerlink" title="自定义Converter"></a>自定义Converter</h2><p>retrofit默认情况下支持的converts有Gson,Jackson,Moshi…</p>
<p>要自定义<code>Converter&lt;F, T&gt;</code>，需要先看一下GsonConverterFactory的实现，<br>GsonConverterFactory实现了内部类Converter.Factory。</p>
<p>其中GsonConverterFactory中的主要两个方法，主要用于解析request和response的，<br>在Factory中还有一个方法stringConverter，用于String的转换。</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="comment">//主要用于响应体的处理，Factory中默认实现为返回null，表示不处理</span></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="comment">/**</span><br><span class="line">  *主要用于请求体的处理，Factory中默认实现为返回null，不能处理返回null</span><br><span class="line">  *作用对象Part、PartMap、Body</span><br><span class="line">  */</span></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></pre></td></tr></table></figure>
<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">//Converter.Factory$stringConverter</span></span><br><span class="line"><span class="comment">/**</span><br><span class="line">  *作用对象Field、FieldMap、Header、Path、Query、QueryMap</span><br><span class="line">  *默认处理是toString</span><br><span class="line">  */</span></span><br><span class="line">  <span class="keyword">public</span> Converter&lt;?, String&gt; stringConverter(Type type, Annotation[] annotations,</span><br><span class="line">          Retrofit retrofit) &#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>GsonRequestBodyConverter实现了<code>Converter&lt;F, T&gt;</code>接口，<br>主要实现了转化的方法</p>
<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="function">T <span class="title">convert</span><span class="params">(F value)</span> <span class="keyword">throws</span> IOException</span>;</span><br></pre></td></tr></table></figure>
<p><a href="https://github.com/BoBoMEe/AndroidDev/tree/retrofit2/retrofit2/htttp/src/main/java/com/bobomee/android/htttp/retrofit2/converfactory" target="_blank" rel="external">StringConverterFactory</a>实现源码</p>
<h2 id="自定义Interceptor"><a href="#自定义Interceptor" class="headerlink" title="自定义Interceptor"></a>自定义Interceptor</h2><p>Retrofit 2.0 底层依赖于okHttp，所以需要使用okHttp的Interceptors 来对所有请求进行拦截。<br>我们可以通过自定义Interceptor来实现很多操作,打印日志,缓存,重试等等。</p>
<p>要实现自己的拦截器需要有以下步骤</p>
<p> (1) 需要实现Interceptor接口，并复写intercept(Chain chain)方法,返回response<br> (2) Request 和 Response的Builder中有header,addHeader,headers方法,需要注意的是使用header有重复的将会被覆盖,而addHeader则不会。</p>
<p>标准的 Interceptor写法</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></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">OAuthInterceptor</span> <span class="keyword">implements</span> <span class="title">Interceptor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> String username;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> String password;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">OAuthInterceptor</span><span class="params">(String username, String password)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.username = username;</span><br><span class="line">    <span class="keyword">this</span>.password = password;</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 <span class="title">intercept</span><span class="params">(Chain chain)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"></span><br><span class="line">    String credentials = username + <span class="string">":"</span> + password;</span><br><span class="line"></span><br><span class="line">    String basic = <span class="string">"Basic "</span> + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);</span><br><span class="line"></span><br><span class="line">    Request originalRequest = chain.request();</span><br><span class="line">    String cacheControl = originalRequest.cacheControl().toString();</span><br><span class="line"></span><br><span class="line">    Request.Builder requestBuilder = originalRequest.newBuilder()</span><br><span class="line">        <span class="comment">//Basic Authentication,也可用于token验证,OAuth验证</span></span><br><span class="line">        .header(<span class="string">"Authorization"</span>, basic)</span><br><span class="line">        .header(<span class="string">"Accept"</span>, <span class="string">"application/json"</span>)</span><br><span class="line">        .method(originalRequest.method(), originalRequest.body());</span><br><span class="line"></span><br><span class="line">    Request request = requestBuilder.build();</span><br><span class="line"></span><br><span class="line">    Response originalResponse = chain.proceed(request);</span><br><span class="line">    Response.Builder responseBuilder =</span><br><span class="line">        <span class="comment">//Cache control设置缓存</span></span><br><span class="line">        originalResponse.newBuilder().header(<span class="string">"Cache-Control"</span>, cacheControl);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> responseBuilder.build();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="缓存策略"><a href="#缓存策略" class="headerlink" title="缓存策略"></a>缓存策略</h2><p>设置缓存就需要用到OkHttp的interceptors，缓存的设置需要靠请求和响应头。<br>如果想要弄清楚缓存机制，则需要了解一下HTTP语义，其中控制缓存的就是<code>Cache-Control</code>字段<br>参考：<a href="http://blog.csdn.net/picasso_l/article/details/50579884" target="_blank" rel="external">Retrofit2.0+okhttp3缓存机制以及遇到的问题</a><br><a href="http://stackoverflow.com/questions/31321963/how-retrofit-with-okhttp-use-cache-data-when-offline" target="_blank" rel="external">How Retrofit with OKHttp use cache data when offline</a><br><a href="http://www.jianshu.com/p/9c3b4ea108a7" target="_blank" rel="external">使用Retrofit和Okhttp实现网络缓存。无网读缓存，有网根据过期时间重新请求</a></p>
<p>一般情况下我们需要达到的缓存效果是这样的:</p>
<ul>
<li>没有网或者网络较差的时候要使用缓存(统一设置)</li>
<li>有网络的时候，要保证不同的需求，实时性数据不用缓存,一般请求需要缓存(单个请求的header来实现)。</li>
</ul>
<p>OkHttp3中有一个Cache类是用来定义缓存的，此类详细介绍了几种缓存策略,具体可看此类源码。</p>
<blockquote>
<p>noCache ：不使用缓存，全部走网络<br> noStore ：  不使用缓存，也不存储缓存<br> onlyIfCached ： 只使用缓存<br> maxAge  ：设置最大失效时间，失效则不使用<br> maxStale ：设置最大失效时间，失效则不使用<br> minFresh ：设置最小有效时间，失效则不使用<br> FORCE_NETWORK ： 强制走网络<br> FORCE_CACHE ：强制走缓存</p>
</blockquote>
<h3 id="配置目录"><a href="#配置目录" class="headerlink" title="配置目录"></a>配置目录</h3><p>这个是缓存文件的存放位置,okhttp默认是没有缓存,且没有缓存目录的。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> Cache <span class="title">cache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//设置缓存路径</span></span><br><span class="line">        <span class="keyword">final</span> File baseDir = AppUtil.getAvailableCacheDir(sContext);</span><br><span class="line">        <span class="keyword">final</span> File cacheDir = <span class="keyword">new</span> File(baseDir, <span class="string">"HttpResponseCache"</span>);</span><br><span class="line">        <span class="comment">//设置缓存 10M</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>其中获取cacahe目录,我们一般采取的策略就是应用卸载,即删除。一般就使用如下两个目录:</p>
<ul>
<li>data/$packageName/cache:Context.getCacheDir()</li>
<li>/storage/sdcard0/Andorid/data/$packageName/cache:Context.getExternalCacheDir()</li>
</ul>
<p>且当sd卡空间小于data可用空间时,使用data目录。</p>
<p>最后来一张图看懂Android内存结构,参考：<a href="http://www.tuicool.com/articles/AvUnqiy" target="_blank" rel="external">Android文件存储使用参考 - liaohuqiu</a></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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line">    * |   ($rootDir)</span><br><span class="line">    * +- /data                    -&gt; Environment.getDataDirectory()</span><br><span class="line">    * |   |</span><br><span class="line">    * |   |   ($appDataDir)</span><br><span class="line">    * |   +- data/$packageName</span><br><span class="line">    * |       |</span><br><span class="line">    * |       |   ($filesDir)</span><br><span class="line">    * |       +- files            -&gt; Context.getFilesDir() / Context.getFileStreamPath("")</span><br><span class="line">    * |       |      |</span><br><span class="line">    * |       |      +- file1     -&gt; Context.getFileStreamPath("file1")</span><br><span class="line">    * |       |</span><br><span class="line">    * |       |   ($cacheDir)</span><br><span class="line">    * |       +- cache            -&gt; Context.getCacheDir()</span><br><span class="line">    * |       |</span><br><span class="line">    * |       +- app_$name        -&gt;(Context.getDir(String name, int mode)</span><br><span class="line">    * |</span><br><span class="line">    * |   ($rootDir)</span><br><span class="line">    * +- /storage/sdcard0         -&gt; Environment.getExternalStorageDirectory()/ Environment.getExternalStoragePublicDirectory("")</span><br><span class="line">    * |                 |</span><br><span class="line">    * |                 +- dir1   -&gt; Environment.getExternalStoragePublicDirectory("dir1")</span><br><span class="line">    * |                 |</span><br><span class="line">    * |                 |   ($appDataDir)</span><br><span class="line">    * |                 +- Andorid/data/$packageName</span><br><span class="line">    * |                                         |</span><br><span class="line">    * |                                         | ($filesDir)</span><br><span class="line">    * |                                         +- files                  -&gt; Context.getExternalFilesDir("")</span><br><span class="line">    * |                                         |    |</span><br><span class="line">    * |                                         |    +- file1             -&gt; Context.getExternalFilesDir("file1")</span><br><span class="line">    * |                                         |    +- Music             -&gt; Context.getExternalFilesDir(Environment.Music);</span><br><span class="line">    * |                                         |    +- Picture           -&gt; Context.getExternalFilesDir(Environment.Picture);</span><br><span class="line">    * |                                         |    +- ...               -&gt; Context.getExternalFilesDir(String type)</span><br><span class="line">    * |                                         |</span><br><span class="line">    * |                                         |  ($cacheDir)</span><br><span class="line">    * |                                         +- cache                  -&gt; Context.getExternalCacheDir()</span><br><span class="line">    * |                                         |</span><br><span class="line">    * |                                         +- ???</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * 1.  其中$appDataDir中的数据，在app卸载之后，会被系统删除。</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * 2.  $appDataDir下的$cacheDir：</span><br><span class="line">    * Context.getCacheDir()：机身内存不足时，文件会被删除</span><br><span class="line">    * Context.getExternalCacheDir()：空间不足时，文件不会实时被删除，可能返回空对象,Context.getExternalFilesDir("")亦同</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * 3. 内部存储中的$appDataDir是安全的，只有本应用可访问</span><br><span class="line">    * 外部存储中的$appDataDir其他应用也可访问，但是$filesDir中的媒体文件，不会被当做媒体扫描出来，加到媒体库中。</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * 4. 在内部存储中：通过  Context.getDir(String name, int mode) 可获取和  $filesDir  /  $cacheDir 同级的目录</span><br><span class="line">    * 命名规则：app_ + name，通过Mode控制目录是私有还是共享</span><br><span class="line">    * &lt;p/&gt;</span><br><span class="line">    * &lt;code&gt;</span><br><span class="line">    * Context.getDir("dir1", MODE_PRIVATE):</span><br><span class="line">    * Context.getDir: /data/data/$packageName/app_dir1</span><br><span class="line">    * &lt;/code&gt;</span><br><span class="line">    */</span></span><br></pre></td></tr></table></figure>
<h3 id="缓存第一种类型"><a href="#缓存第一种类型" class="headerlink" title="缓存第一种类型"></a>缓存第一种类型</h3><p>配置单个请求的@Headers，设置此请求的缓存策略,不影响其他请求的缓存策略,不设置则没有缓存。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置 单个请求的 缓存时间</span></span><br><span class="line"><span class="meta">@Headers</span>(<span class="string">"Cache-Control: max-age=640000"</span>)</span><br><span class="line"><span class="meta">@GET</span>(<span class="string">"widget/list"</span>)</span><br><span class="line">Call&lt;List&lt;Widget&gt;&gt; widgetList();</span><br></pre></td></tr></table></figure>
<h3 id="缓存第二种类型"><a href="#缓存第二种类型" class="headerlink" title="缓存第二种类型"></a>缓存第二种类型</h3><p>有网和没网都先读缓存，统一缓存策略，降低服务器压力。</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">private</span> Interceptor <span class="title">cacheInterceptor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      Interceptor cacheInterceptor = <span class="keyword">new</span> Interceptor() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> Response <span class="title">intercept</span><span class="params">(Chain chain)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">                Request request = chain.request();</span><br><span class="line">                Response response = chain.proceed(request);</span><br><span class="line"></span><br><span class="line">                String cacheControl = request.cacheControl().toString();</span><br><span class="line">                <span class="keyword">if</span> (TextUtils.isEmpty(cacheControl)) &#123;</span><br><span class="line">                    cacheControl = <span class="string">"public, max-age=60"</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> response.newBuilder()</span><br><span class="line">                        .header(<span class="string">"Cache-Control"</span>, cacheControl)</span><br><span class="line">                        .removeHeader(<span class="string">"Pragma"</span>)</span><br><span class="line">                        .build();</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>此中方式的缓存Interceptor实现：<a href="https://github.com/BoBoMEe/AndroidDev/blob/retrofit2/retrofit2/htttp/src/main/java/com/bobomee/android/htttp/okhttp/interceptor/ForceCachedInterceptor.java" target="_blank" rel="external">ForceCachedInterceptor.java</a></p>
<h3 id="缓存第三种类型"><a href="#缓存第三种类型" class="headerlink" title="缓存第三种类型"></a>缓存第三种类型</h3><p>结合前两种，离线读取本地缓存，在线获取最新数据(读取单个请求的请求头，亦可统一设置)。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Interceptor <span class="title">cacheInterceptor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Interceptor() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> Response <span class="title">intercept</span><span class="params">(Chain chain)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">                Request request = chain.request();</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (!AppUtil.isNetworkReachable(sContext)) &#123;</span><br><span class="line">                    request = request.newBuilder()</span><br><span class="line">                            <span class="comment">//强制使用缓存</span></span><br><span class="line">                            .cacheControl(CacheControl.FORCE_CACHE)</span><br><span class="line">                            .build();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                Response response = chain.proceed(request);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (AppUtil.isNetworkReachable(sContext)) &#123;</span><br><span class="line">                    <span class="comment">//有网的时候读接口上的@Headers里的配置，你可以在这里进行统一的设置</span></span><br><span class="line">                    String cacheControl = request.cacheControl().toString();</span><br><span class="line">                    Logger.i(<span class="string">"has network ,cacheControl="</span> + cacheControl);</span><br><span class="line">                    <span class="keyword">return</span> response.newBuilder()</span><br><span class="line">                            .header(<span class="string">"Cache-Control"</span>, cacheControl)</span><br><span class="line">                            .removeHeader(<span class="string">"Pragma"</span>)</span><br><span class="line">                            .build();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">int</span> maxStale = <span class="number">60</span> * <span class="number">60</span> * <span class="number">24</span> * <span class="number">28</span>; <span class="comment">// tolerate 4-weeks stale</span></span><br><span class="line">                    Logger.i(<span class="string">"network error ,maxStale="</span>+maxStale);</span><br><span class="line">                    <span class="keyword">return</span> response.newBuilder()</span><br><span class="line">                            .header(<span class="string">"Cache-Control"</span>, <span class="string">"public, only-if-cached, max-stale="</span>+maxStale)</span><br><span class="line">                            .removeHeader(<span class="string">"Pragma"</span>)</span><br><span class="line">                            .build();</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">    &#125;</span><br></pre></td></tr></table></figure>
<p>此中方式的缓存Interceptor实现：<a href="https://github.com/BoBoMEe/AndroidDev/blob/retrofit2/retrofit2/htttp/src/main/java/com/bobomee/android/htttp/okhttp/interceptor/OfflineCacheControlInterceptor.java" target="_blank" rel="external">OfflineCacheControlInterceptor.java</a></p>
<h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><p>在请求网络的时候,我们不止会得到HttpException,还有我们和服务器约定的errorCode和errorMessage,为了统一处理,我们可以<br>预处理以下上面两个字段,定义BaseModel,在ConverterFactory中进行处理,<br>可参照:</p>
<ul>
<li><a href="http://blog.csdn.net/efan006/article/details/50544204" target="_blank" rel="external">Retrofit+RxJava实战日志(3)-网络异常处理</a></li>
<li><a href="https://futurestud.io/blog/retrofit-2-simple-error-handling" target="_blank" rel="external">retrofit-2-simple-error-handling</a></li>
</ul>
<h2 id="网络状态监听"><a href="#网络状态监听" class="headerlink" title="网络状态监听"></a>网络状态监听</h2><p>一般在没有网络的时候使用缓存数据,有网络的时候及时重试获取最新数据,其中获取是否有网络，我们采用广播的形式：</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></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">NetWorkReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</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">onReceive</span><span class="params">(Context context, Intent intent)</span> </span>&#123;</span><br><span class="line">        HttpNetUtil.INSTANCE.setConnected(context);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>HttpNetUtil实时获取网络连接状态,关键代码</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line">  * 获取是否连接</span><br><span class="line">  */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isConnected</span><span class="params">()</span> </span>&#123;</span><br><span class="line">     <span class="keyword">return</span> isConnected;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">/**</span><br><span class="line">  * 判断网络连接是否存在</span><br><span class="line">  *</span><br><span class="line">  * <span class="doctag">@param</span> context</span><br><span class="line">  */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setConnected</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">     ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);</span><br><span class="line">     <span class="keyword">if</span> (manager == <span class="keyword">null</span>) &#123;</span><br><span class="line">         setConnected(<span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">         <span class="keyword">if</span> (networkreceivers != <span class="keyword">null</span>) &#123;</span><br><span class="line">             <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, z = networkreceivers.size(); i &lt; z; i++) &#123;</span><br><span class="line">                 Networkreceiver listener = networkreceivers.get(i);</span><br><span class="line">                 <span class="keyword">if</span> (listener != <span class="keyword">null</span>) &#123;</span><br><span class="line">                     listener.onConnected(<span class="keyword">false</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">     &#125;</span><br><span class="line"></span><br><span class="line">     NetworkInfo info = manager.getActiveNetworkInfo();</span><br><span class="line"></span><br><span class="line">     <span class="keyword">boolean</span> connected = info != <span class="keyword">null</span> &amp;&amp; info.isConnected();</span><br><span class="line">     setConnected(connected);</span><br><span class="line"></span><br><span class="line">     <span class="keyword">if</span> (networkreceivers != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, z = networkreceivers.size(); i &lt; z; i++) &#123;</span><br><span class="line">             Networkreceiver listener = networkreceivers.get(i);</span><br><span class="line">             <span class="keyword">if</span> (listener != <span class="keyword">null</span>) &#123;</span><br><span class="line">                 listener.onConnected(connected);</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"> &#125;</span><br></pre></td></tr></table></figure>
<p>在需要监听网络的界面或者base(需要判断当前activity是否在栈顶)实现Networkreceiver。</p>
<h2 id="Retrofit封装"><a href="#Retrofit封装" class="headerlink" title="Retrofit封装"></a>Retrofit封装</h2><p>全局单利的OkHttpClient：</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></pre></td><td class="code"><pre><span class="line">okHttp() &#123;</span><br><span class="line">        HttpLoggingInterceptor interceptor = <span class="keyword">new</span> HttpLoggingInterceptor();</span><br><span class="line">        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);</span><br><span class="line"></span><br><span class="line">        okHttpClient = <span class="keyword">new</span> OkHttpClient.Builder()</span><br><span class="line">                <span class="comment">//打印日志</span></span><br><span class="line">                .addInterceptor(interceptor)</span><br><span class="line"></span><br><span class="line">                <span class="comment">//设置Cache目录</span></span><br><span class="line">                .cache(CacheUtil.getCache(UIUtil.getContext()))</span><br><span class="line"></span><br><span class="line">                <span class="comment">//设置缓存</span></span><br><span class="line">                .addInterceptor(cacheInterceptor)</span><br><span class="line">                .addNetworkInterceptor(cacheInterceptor)</span><br><span class="line"></span><br><span class="line">                <span class="comment">//失败重连</span></span><br><span class="line">                .retryOnConnectionFailure(<span class="keyword">true</span>)</span><br><span class="line"></span><br><span class="line">                <span class="comment">//time out</span></span><br><span class="line">                .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)</span><br><span class="line">                .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)</span><br><span class="line"></span><br><span class="line">                .build()</span><br><span class="line"></span><br><span class="line">        ;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>全局单利的Retrofit.Builder,这里返回builder是为了方便我们设置baseUrl的,我们可以动态创建多个api接口,当然也可以用@Url注解</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">Retrofit2Client() &#123;</span><br><span class="line">        retrofitBuilder = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line">                <span class="comment">//设置OKHttpClient</span></span><br><span class="line">                .client(okHttp.INSTANCE.getOkHttpClient())</span><br><span class="line"></span><br><span class="line">                <span class="comment">//Rx</span></span><br><span class="line">                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())</span><br><span class="line"></span><br><span class="line">                <span class="comment">//String转换器</span></span><br><span class="line">                .addConverterFactory(StringConverterFactory.create())</span><br><span class="line"></span><br><span class="line">                <span class="comment">//gson转化器</span></span><br><span class="line">                .addConverterFactory(GsonConverterFactory.create())</span><br><span class="line">        ;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>Retrofit2+RxJava 使用Demo：<a href="https://github.com/BoBoMEe/AndroidDev/tree/retrofit2/retrofit2" target="_blank" rel="external">Retrofit2Demo</a></p>
<p>参考：<br><a href="https://futurestud.io/blog/tag/retrofit" target="_blank" rel="external">Articles tagged in: Retrofit</a></p>
<p><a href="http://square.github.io/retrofit/#restadapter-configuration" target="_blank" rel="external">官方文档</a></p>
<p><a href="http://blog.csdn.net/lmj623565791/article/details/51304204" target="_blank" rel="external">Retrofit2 完全解析 探索与okhttp之间的关系</a></p>
<p><a href="https://drakeet.me/retrofit-2-0-okhttp-3-0-config" target="_blank" rel="external">Retrofit 2.0 + OkHttp 3.0 配置</a></p>
<p><a href="http://blog.csdn.net/tiankong1206/article/details/50720758" target="_blank" rel="external">更新到Retrofit2的一些技巧</a></p>
<p><a href="http://omgitsmgp.com/2015/12/02/effective-okhttp/" target="_blank" rel="external">Effective OkHttp</a></p>
<p><a href="http://www.jianshu.com/p/2710ed1e6b48" target="_blank" rel="external">Okhttp-wiki 之 Interceptors 拦截器</a></p>
<p><a href="http://blog.csdn.net/picasso_l/article/details/50579884" target="_blank" rel="external">Retrofit2.0+okhttp3缓存机制以及遇到的问题</a></p>
<p><a href="http://stackoverflow.com/questions/31321963/how-retrofit-with-okhttp-use-cache-data-when-offline" target="_blank" rel="external">How Retrofit with OKHttp use cache data when offline</a></p>
<p><a href="http://www.jianshu.com/p/9c3b4ea108a7" target="_blank" rel="external">使用Retrofit和Okhttp实现网络缓存。无网读缓存，有网根据过期时间重新请求</a></p>
<p><a href="https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/" target="_blank" rel="external">用 Retrofit 2 简化 HTTP 请求</a></p>
<p><a href="http://www.loongwind.com/archives/242.html" target="_blank" rel="external">Retrofit请求参数注解字段说明</a></p>
<p><a href="http://www.tuicool.com/articles/AvUnqiy" target="_blank" rel="external">Android文件存储使用参考 - liaohuqiu</a></p>
<p><a href="http://blog.csdn.net/efan006/article/details/50544204" target="_blank" rel="external">Retrofit+RxJava实战日志(3)-网络异常处理</a></p>
<p><a href="https://futurestud.io/blog/retrofit-2-simple-error-handling" target="_blank" rel="external">retrofit-2-simple-error-handling</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;随着Google对HttpClient 摒弃,和Volley的逐渐没落,OkHttp开始异军突起,而Retrofit则对okHttp进行了强制依赖。&lt;/p&gt;
&lt;p&gt;Retrofit是由&lt;a href=&quot;https://github.com/square&quot;&gt;Square&lt;/a&gt;公司出品的针对于Android和Java的类型安全的Http客户端，&lt;/p&gt;
&lt;p&gt;如果看源码会发现其实质上就是对okHttp的封装，使用面向接口的方式进行网络请求，利用动态生成的代理类封装了网络接口请求的底层,&lt;/p&gt;
&lt;p&gt;其将请求返回javaBean，对网络认证 REST API进行了很好对支持此，使用Retrofit将会极大的提高我们应用的网络体验。&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="retrofit" scheme="http://bobomee.github.io/tags/retrofit/"/>
    
  </entry>
  
  <entry>
    <title>git常用命令</title>
    <link href="http://bobomee.github.io/2016/05/07/gitbasic/"/>
    <id>http://bobomee.github.io/2016/05/07/gitbasic/</id>
    <published>2016-05-07T03:07:46.000Z</published>
    <updated>2017-05-14T11:06:38.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>git 作为我们常用的版本管理工具，提供了丰富的命令操作,这里记录一下开发中我们常用的命令，以备不时之需。</p>
<a id="more"></a>
<h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>首先，安装完git后，我们需要用下面的命令来配置git。</p>
<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">$ git config --global user.name <span class="string">"Your Name"</span></span><br><span class="line">$ git config --global user.email <span class="string">"email@example.com"</span></span><br></pre></td></tr></table></figure>
<h2 id="查看"><a href="#查看" class="headerlink" title="查看"></a>查看</h2><ul>
<li><code>pwd</code> :用于显示当前目录.<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><br><span class="line">BoBoMEedeMacBook-Pro:RxJavaLearn bobomee$ pwd</span><br><span class="line">/Users/bobomee/Desktop/rx/RxJavaLearn</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="时光穿梭"><a href="#时光穿梭" class="headerlink" title="时光穿梭"></a>时光穿梭</h2><ul>
<li><code>git reflog</code>:查看命令历史,<code>commit id</code>，用于回到未来的个版本<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">$ git reflog</span><br><span class="line">ea34578 HEAD@&#123;<span class="number">0</span>&#125;: reset: moving to HEAD^</span><br><span class="line"><span class="number">3628164</span> HEAD@&#123;<span class="number">1</span>&#125;: commit: append GPL</span><br><span class="line">ea34578 HEAD@&#123;<span class="number">2</span>&#125;: commit: add distributed</span><br><span class="line">cb926e7 HEAD@&#123;<span class="number">3</span>&#125;: commit (initial): wrote a readme file</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="撤销修改-git-status查看"><a href="#撤销修改-git-status查看" class="headerlink" title="撤销修改(git status查看)"></a>撤销修改(<code>git status</code>查看)</h2><ul>
<li><code>git checkout -- file</code>:撤销file的修改到最近一次<code>add/commit</code>的状态<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><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">$ git status</span><br><span class="line"># On branch master</span><br><span class="line"># Changes not staged for commit:</span><br><span class="line">#   (use "git add &lt;file&gt;..." to update what will be committed)</span><br><span class="line">#   (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)</span><br><span class="line">#</span><br><span class="line">#       modified:   readme.txt</span><br><span class="line">#</span><br><span class="line">no changes added to commit (use "git add" and/or "git commit -a")</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>同时，如果误删文件，用此命令可以恢复</p>
<ul>
<li><code>git reset HEAD file</code>:撤销暂存区的修改（unstage），重新放回工作区.其中<code>HEAD</code>，表示最新的版本<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><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">$ git status</span><br><span class="line"># On branch master</span><br><span class="line"># Changes to be committed:</span><br><span class="line">#   (use "git reset HEAD &lt;file&gt;..." to unstage)</span><br><span class="line">#</span><br><span class="line">#       modified:   readme.txt</span><br><span class="line">#</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h2><p>本地与远程库使用ssh加密，需要配置<code>SSH Key</code>。</p>
<p>通过如下命令，查看是否存在<code>id_rsa.pub</code><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">$ ls -al ~/.ssh</span><br></pre></td></tr></table></figure></p>
<p>如果没有的话，通过如下命令来生成</p>
<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">$ ssh-keygen -t rsa -C <span class="string">"email@example.com"</span></span><br></pre></td></tr></table></figure>
<p>如果有的话，则使用如下命令来查看</p>
<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">$ cd ~/.ssh</span><br><span class="line">$ cat id_rsa.pub</span><br></pre></td></tr></table></figure>
<p>将SSH添加到远程库后，使用如下命令来测试是否可用</p>
<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">$ ssh -T git<span class="meta">@github</span>.com</span><br></pre></td></tr></table></figure>
<p>与远程仓库关联</p>
<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">$ git remote add origin git<span class="meta">@github</span>.com:BoBoMEe/bobomee.github.io.git</span><br></pre></td></tr></table></figure>
<p>移除远程仓库</p>
<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">$ git remote rm origin</span><br></pre></td></tr></table></figure>
<p>查看远程仓库</p>
<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">$ git remote -v</span><br></pre></td></tr></table></figure>
<p>推送到远程库</p>
<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">$ git push origin master</span><br></pre></td></tr></table></figure>
<p>第一次使用，使用参数<code>u</code>与远程仓库联系起来</p>
<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">$ git push -u origin master</span><br></pre></td></tr></table></figure>
<h2 id="分支管理"><a href="#分支管理" class="headerlink" title="分支管理"></a>分支管理</h2><p>常用命令如下：</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></pre></td><td class="code"><pre><span class="line">$ git branch <span class="comment">// 查看分支</span></span><br><span class="line"></span><br><span class="line">$ git branch &lt;name&gt; <span class="comment">//创建分支</span></span><br><span class="line"></span><br><span class="line">$ git checkout &lt;name&gt; <span class="comment">//切换分支</span></span><br><span class="line"></span><br><span class="line">$ git checkout -b &lt;name&gt; <span class="comment">//创建+切换分支</span></span><br><span class="line"></span><br><span class="line">$ git merge &lt;name&gt; <span class="comment">//合并某分支到当前分支</span></span><br><span class="line"></span><br><span class="line">$ git branch -d &lt;name&gt; <span class="comment">//删除分支</span></span><br><span class="line">$ git branch -D &lt;name&gt; <span class="comment">//强行删除，适用尚未merge的分支</span></span><br><span class="line"></span><br><span class="line">$ git log --graph  <span class="comment">//看到分支合并图</span></span><br></pre></td></tr></table></figure>
<h3 id="分支管理策略"><a href="#分支管理策略" class="headerlink" title="分支管理策略"></a>分支管理策略</h3><p><code>--no-ff</code>模式，会在merge时生成一个新的commit，而<code>fast forward(Default)</code>j没有，<br>用<code>--no-ff</code>模式方便以后查看历史</p>
<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">$ git merge --no-ff -m <span class="string">"merge with no-ff"</span> dev</span><br></pre></td></tr></table></figure>
<ul>
<li>常用分支<br></li>
</ul>
<p><code>master</code>稳定分支，仅用来发布新版本，平时不在上面干活</p>
<p><code>dev</code>开发分支，团队成员每个人都在自己到分支上工作，并merge到dev，开发完成合并到master</p>
<ul>
<li>临时分支</li>
</ul>
<p>bug（fixbug）分支,紧急修复bug用，最终合并到master和dev，并删除，不推送到远程</p>
<p>预发布（release）分支，合并到master之前的测试版本，最终合并到dev和master，并删除</p>
<p>功能（feature）分支，用于新功能开发，最终合并到dev，并删除</p>
<h3 id="工作区保存"><a href="#工作区保存" class="headerlink" title="工作区保存"></a>工作区保存</h3><p>比如正在dev分支进行开发，需要新建bug分支来修复bug，开发尚未完成，不好提交<br>此时需要用到<code>stash</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 把当前工作现场“储藏”起来，等以后恢复现场后继续工作</span></span><br><span class="line">$ git stash</span><br><span class="line">Saved working directory and index state WIP on dev: <span class="number">6224937</span> add merge</span><br><span class="line">HEAD is now at <span class="number">6224937</span> add merge</span><br></pre></td></tr></table></figure>
<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="comment">// 查看stash 列表</span></span><br><span class="line">$ git stash list</span><br><span class="line">stash@&#123;<span class="number">0</span>&#125;: WIP on dev: <span class="number">6224937</span> add merge</span><br></pre></td></tr></table></figure>
<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><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">//恢复stash的，并删除stash内容</span><br><span class="line">$ git stash pop</span><br><span class="line"># On branch dev</span><br><span class="line"># Changes to be committed:</span><br><span class="line">#   (use "git reset HEAD &lt;file&gt;..." to unstage)</span><br><span class="line">#</span><br><span class="line">#       new file:   hello.py</span><br><span class="line">#</span><br><span class="line"># Changes not staged for commit:</span><br><span class="line">#   (use "git add &lt;file&gt;..." to update what will be committed)</span><br><span class="line">#   (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)</span><br><span class="line">#</span><br><span class="line">#       modified:   readme.txt</span><br><span class="line">#</span><br><span class="line">Dropped refs/stash@&#123;0&#125; (f624f8e5f082f2df2bed8a4e09c12fd2943bdd40)</span><br></pre></td></tr></table></figure>
<p>使用<code>git stash apply</code>，stash内容并不删除，使用<code>git stash drop</code>来删除</p>
<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">//恢复指定的stash</span></span><br><span class="line">$ git stash apply stash@&#123;<span class="number">0</span>&#125;</span><br></pre></td></tr></table></figure>
<h2 id="多人协作"><a href="#多人协作" class="headerlink" title="多人协作"></a>多人协作</h2><h3 id="贡献开源"><a href="#贡献开源" class="headerlink" title="贡献开源"></a>贡献开源</h3><p>github上众多的开源项目都是用git来进行管理的，我们可以通过fork/pull request<br>来贡献开源，标准的操作流程中，我们需要注意以下一些地方。</p>
<ul>
<li>不要在master上进行更改，为我们的主题创建一个单独的branch。</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">$ git checkout -b fixbug</span><br></pre></td></tr></table></figure>
<ul>
<li>rebase与merge</li>
</ul>
<p>比如源仓库有更新时，最好使用rebase，这样提交pullrequest的时候不会产生多余的commit</p>
<ul>
<li>同步源仓库修改</li>
</ul>
<p>如果上游仓库更新，这时候我们想要新的特性的时候，可以使用如下命令</p>
<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">$ git branch --set-upstream-to=&lt;repo_name&gt;/master master</span><br></pre></td></tr></table></figure>
<h3 id="团队开发"><a href="#团队开发" class="headerlink" title="团队开发"></a>团队开发</h3><p>如果是团队开发的话，一般都会在dev分支上干活，从远程库上clone下来后，在本地默认只会看到master分支</p>
<p>ps:命令查看<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">$ git branch</span><br><span class="line">* master</span><br></pre></td></tr></table></figure></p>
<p>所以必须在本地创建和远程分支对应的分支dev，并和远程关联。</p>
<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">$ git checkout -b dev origin/dev</span><br></pre></td></tr></table></figure>
<p>在推送到远程库之前，pull的时候，如果出现<code>no tracking information</code>,<br>则可以使用如下命令,建立本地分支和远程分支的关联</p>
<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">$ git branch --set-upstream-to=&lt;repo_name&gt;/master master</span><br></pre></td></tr></table></figure>
<p>参考：</p>
<p><a href="http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013760174128707b935b0be6fc4fc6ace66c4f15618f8d000" target="_blank" rel="external">廖雪峰的官方网站</a></p>
<p><a href="http://blog.csdn.net/zhangdaiscott/article/details/17438153" target="_blank" rel="external">Pull Request的正确打开方式（如何在GitHub上贡献开源项目</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;git 作为我们常用的版本管理工具，提供了丰富的命令操作,这里记录一下开发中我们常用的命令，以备不时之需。&lt;/p&gt;
    
    </summary>
    
      <category term="开发工具" scheme="http://bobomee.github.io/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="git" scheme="http://bobomee.github.io/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>github pages ＋ hexo ＋ next搭建个人博客</title>
    <link href="http://bobomee.github.io/2016/05/04/hexoblog/"/>
    <id>http://bobomee.github.io/2016/05/04/hexoblog/</id>
    <published>2016-05-04T13:07:46.000Z</published>
    <updated>2017-05-14T11:10:03.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>github pages原本是github提供给开源项目做项目介绍的，既然是静态页面，当然拿来做blog既免费又方便了，现在流行用hexo框架 ＋ next主题，鉴于本人在搭建过程中踩过的坑，这里记录一下，希望能够方便后来人。</p>
<a id="more"></a>
<h2 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h2><h3 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h3><p>Mac自带，windows需要下载msysgit。为了后期方便，最好是配置好ssh等信息。</p>
<p>首先在github创建项目<code>bobomee.github.io</code>，这里仓库名规则是一定的。</p>
<h3 id="Node-js"><a href="#Node-js" class="headerlink" title="Node.js"></a>Node.js</h3><p>mac下使用brew命令安装<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"><span class="comment">// 安装 node</span></span><br><span class="line">brew install node</span><br></pre></td></tr></table></figure></p>
<p>如果出现错误，尝试使用如下命令<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">sudo chown -R $(whoami):admin /usr/local</span><br></pre></td></tr></table></figure></p>
<p>查看是否安装成功<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">node -v</span><br></pre></td></tr></table></figure></p>
<h3 id="hexo"><a href="#hexo" class="headerlink" title="hexo"></a>hexo</h3><p>使用npm来安装hexo<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"><span class="comment">// npm在git bash中使用</span></span><br><span class="line">sudo npm install -g hexo</span><br></pre></td></tr></table></figure></p>
<p>这里有坑，官网命令：<code>npm install -g hexo-cli</code>会出现权限错误。</p>
<p>查看是否安装成功<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">hexo -v</span><br></pre></td></tr></table></figure></p>
<h2 id="初始化blog"><a href="#初始化blog" class="headerlink" title="初始化blog"></a>初始化blog</h2><h3 id="hexo-init"><a href="#hexo-init" class="headerlink" title="hexo init"></a>hexo init</h3><p>新建blog目录，为方便操作，在桌面上新建目录<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">ls</span><br><span class="line">cd Desktop/</span><br><span class="line">mkdir hexo</span><br><span class="line">cd hexo/</span><br><span class="line">hexo init</span><br><span class="line">npm install</span><br></pre></td></tr></table></figure></p>
<p>开启本地服务预览<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">hexo s</span><br></pre></td></tr></table></figure></p>
<p>此命令执行后，就可以通过<code>http://localhost:4000</code>查看博客了。</p>
<h3 id="config-blog"><a href="#config-blog" class="headerlink" title="config blog"></a>config blog</h3><p>此时会看到hexo目录下文件<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">_config.yml    </span><br><span class="line">db.json</span><br><span class="line">node_modules</span><br><span class="line"><span class="keyword">package</span>.json</span><br><span class="line">scaffolds</span><br><span class="line">source</span><br><span class="line">themes</span><br></pre></td></tr></table></figure></p>
<p>_config.yml :是全局配置文件，在最后加上如下语句和github关联起来<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></pre></td><td class="code"><pre><span class="line">deploy:</span><br><span class="line">    type: git</span><br><span class="line">    repository: https:<span class="comment">//github.com/BoBoMEe/bobomee.github.io.git</span></span><br><span class="line">    branch: master</span><br></pre></td></tr></table></figure></p>
<p>这里有坑：需要注意冒号后面需要有空格，其实这个文件的所有冒号后面都需要加上空格。</p>
<h3 id="deploy"><a href="#deploy" class="headerlink" title="deploy"></a>deploy</h3><p>推送到github，可以用如下命令</p>
<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">hexo g <span class="comment">//generate</span></span><br><span class="line">hexo d <span class="comment">//deploy</span></span><br></pre></td></tr></table></figure>
<p>这里有坑：如果更改后需要先<code>hexo clean</code>之后再重新生成网页,同时修改了_config.yml中的deploy信息，需要先调用<code>npm install hexo-deployer-git --save</code></p>
<h2 id="主题设置"><a href="#主题设置" class="headerlink" title="主题设置"></a>主题设置</h2><p>使用hexo比较流行的next主题，配置可见<a href="https://github.com/iissnan/hexo-theme-next" target="_blank" rel="external">hexo-theme-next</a></p>
<h2 id="源码托管"><a href="#源码托管" class="headerlink" title="源码托管"></a>源码托管</h2><p>如果我们不在一个地方写blog的话，可以尝试将blog的source也托管到分支上</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></pre></td><td class="code"><pre><span class="line">ls</span><br><span class="line">cd Desktop/</span><br><span class="line">cd hexo/</span><br><span class="line">git init</span><br><span class="line">git checkout --orphan gh-pages</span><br><span class="line">git add .</span><br><span class="line">git commit -m <span class="string">"blog source"</span></span><br><span class="line">git remote add origin https:<span class="comment">//github.com/BoBoMEe/bobomee.github.io.git</span></span><br><span class="line">git push origin gh-pages</span><br></pre></td></tr></table></figure>
<h2 id="发表文章"><a href="#发表文章" class="headerlink" title="发表文章"></a>发表文章</h2><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">hexo <span class="keyword">new</span> <span class="string">"postName"</span></span><br></pre></td></tr></table></figure>
<p>会在<code>/blog/source/_posts</code>生成postName.md,也可以直接新建。</p>
<h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</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></pre></td><td class="code"><pre><span class="line">hexo <span class="keyword">new</span> <span class="string">"这是一篇新文章"</span><span class="comment">// 新建文档</span></span><br><span class="line">hexo clean<span class="comment">//清空缓存</span></span><br><span class="line">hexo g<span class="comment">// 生成静态页面</span></span><br><span class="line">hexo s<span class="comment">// 预览, 可打开浏览器进行预览</span></span><br><span class="line">hexo d<span class="comment">// 发布到 github ，需在站点配置文件中配置正确</span></span><br></pre></td></tr></table></figure>
<p>更多hexo和thene配置参考：</p>
<p><a href="http://www.jianshu.com/p/858ecf233db9" target="_blank" rel="external">hexo配置</a></p>
<p><a href="https://github.com/iissnan/hexo-theme-next" target="_blank" rel="external">hexo-theme-next</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;github pages原本是github提供给开源项目做项目介绍的，既然是静态页面，当然拿来做blog既免费又方便了，现在流行用hexo框架 ＋ next主题，鉴于本人在搭建过程中踩过的坑，这里记录一下，希望能够方便后来人。&lt;/p&gt;
    
    </summary>
    
      <category term="开发工具" scheme="http://bobomee.github.io/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="hexo" scheme="http://bobomee.github.io/tags/hexo/"/>
    
  </entry>
  
  <entry>
    <title>快速开发之Android Orm总结</title>
    <link href="http://bobomee.github.io/2016/04/20/androidorm/"/>
    <id>http://bobomee.github.io/2016/04/20/androidorm/</id>
    <published>2016-04-20T14:02:00.000Z</published>
    <updated>2017-05-14T11:05:26.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>在android平台上，常用的数据保存方式有Preference，文件和数据库，针对于数据库操作，<br>一般都会采用orm框架来解决问题，Android中使用比较广泛的Orm有LiteOrm,OrmLite，SugarORM，GreenDAO，ActiveAndroid，Realm等</p>
<p>AndroidOrm使用的小demo:<a href="https://github.com/BoBoMEe/AndroidDev/tree/orm" target="_blank" rel="external">AndroidOrm</a></p>
<a id="more"></a>
<h2 id="LiteOrm"><a href="#LiteOrm" class="headerlink" title="LiteOrm"></a>LiteOrm</h2><p>这款和<code>OrmLite</code>不是一个东西,项目地址:<a href="https://github.com/litesuits/android-lite-orm,是一个专注于数据库操作的Orm" target="_blank" rel="external">https://github.com/litesuits/android-lite-orm,是一个专注于数据库操作的Orm</a></p>
<p>提供了非常方便的 <code>级联操作</code> 和 <code>单独操作</code> 切换,<code>LiteOrm对象</code> 需要保持全局单例, 其提供了非常丰富的注解(Annotation)</p>
<p>CRUD操作也十分方便,应对数据库的升级也不需要做任何操作.只需修改<code>Model</code>即可,因为<code>LiteOrm</code>自动探测技术</p>
<p>详细使用:<a href="http://www.jianshu.com/p/0d72226ef434" target="_blank" rel="external">Android 快速开发系列之数据库篇（LiteOrm</a></p>
<p>和其他Orm的对比:<a href="http://www.jianshu.com/p/330bbd3b0e68" target="_blank" rel="external">Android数据库框架：greenDAO vs LiteOrm</a></p>
<h2 id="OrmLite"><a href="#OrmLite" class="headerlink" title="OrmLite"></a>OrmLite</h2><p><code>OrmLite</code>是java平台的Orm框架,可以在任何使用Java的地方,支持JDBC，Spring等，用到了很多注解 (Annotation),<br>官网地址<a href="http://ormlite.com/" target="_blank" rel="external">http://ormlite.com/</a></p>
<p>启用方式: 第一步,编写JavaBean</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DatabaseTable</span>(tableName = <span class="string">"users"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>&#123;</span><br><span class="line">    <span class="meta">@DatabaseField</span>(id = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="meta">@DatabaseField</span></span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ORMLite needs a no-arg constructor</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">(String username, String password)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.username = username;</span><br><span class="line">        <span class="keyword">this</span>.password = password;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Implementing getter and setter methods</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>第二步: 编写<code>DatabaseHelper</code>,继承<code>OrmLiteSqliteOpenHelper</code>,具体查看Sample,也十分简单</p>
<p>第三步: 通过<code>helper.getUserDao()</code>来进行CRUD操作</p>
<p>create(user),deleteById(id),update(user),queryForAll()等.</p>
<p>详细使用:<a href="http://www.jianshu.com/p/05782b598cf0" target="_blank" rel="external">Android 数据库框架ormlite 使用精要</a></p>
<h2 id="SugarORM"><a href="#SugarORM" class="headerlink" title="SugarORM"></a>SugarORM</h2><p>SugarORM是android 专用orm，提供save(), delete() 和 find()/findById()方法，官网地址:<a href="https://github.com/satyan/sugar" target="_blank" rel="external">https://github.com/satyan/sugar</a></p>
<p>启用方式：第一步:配置meta-data<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></pre></td><td class="code"><pre><span class="line">&lt;meta-data android:name=<span class="string">"DATABASE"</span> android:value=<span class="string">"my_database.db"</span> /&gt;</span><br><span class="line">&lt;meta-data android:name=<span class="string">"VERSION"</span> android:value=<span class="string">"1"</span> /&gt;</span><br><span class="line">&lt;meta-data android:name=<span class="string">"QUERY_LOG"</span> android:value=<span class="string">"true"</span> /&gt;</span><br><span class="line">&lt;meta-data android:name=<span class="string">"DOMAIN_PACKAGE_NAME"</span> android:value=<span class="string">"com.my-domain"</span> /&gt;</span><br></pre></td></tr></table></figure></p>
<p>第二步: JavaBean,需要继承SugarRecord,或者使用@Table注解<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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">extends</span> <span class="title">SugarRecord</span>&lt;<span class="title">User</span>&gt; </span>&#123;</span><br><span class="line">    String username;</span><br><span class="line">    String password;</span><br><span class="line">    <span class="keyword">int</span> age;</span><br><span class="line">    <span class="meta">@Ignore</span></span><br><span class="line">    String bio; <span class="comment">//this will be ignored by SugarORM</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">()</span> </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">(String username, String password,<span class="keyword">int</span> age)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.username = username;</span><br><span class="line">        <span class="keyword">this</span>.password = password;</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></pre></td></tr></table></figure></p>
<p>CRUD: 通过save(), delete() 和 find()方法完成<br>其中update操作.</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></pre></td><td class="code"><pre><span class="line">Book book = Book.findById(Book.class, <span class="number">1</span>);</span><br><span class="line">book.title = <span class="string">"updated title here"</span>; <span class="comment">// modify the values</span></span><br><span class="line">book.edition = <span class="string">"3rd edition"</span>;</span><br><span class="line">book.save(); <span class="comment">// updates the previous entry with new values.</span></span><br></pre></td></tr></table></figure>
<p>上手很简单,更多用法参考官方文档:<a href="http://satyan.github.io/sugar/getting-started.html" target="_blank" rel="external">http://satyan.github.io/sugar/getting-started.html</a></p>
<h2 id="GreenDAO"><a href="#GreenDAO" class="headerlink" title="GreenDAO"></a>GreenDAO</h2><p>GreenDAO 号称Android上最快的orm框架，每秒数千条记录。官网地址:<a href="http://greenrobot.org/greendao/" target="_blank" rel="external">http://greenrobot.org/greendao/</a></p>
<p>作为一款老字号的Orm框架,网上的介绍也比较多了,需要建立Java工程来生成DAO、Master、Session 等对象<br>如果数据库升级,需要重新创建DaoGenerator,来生成代码.</p>
<p>其后的CRUD操作都是依赖于Dao来完成.和其他Orm没有本质的区别.</p>
<p>详细使用见官方文档:<a href="http://greenrobot.org/greendao/documentation/" target="_blank" rel="external">http://greenrobot.org/greendao/documentation/</a></p>
<h2 id="ActiveAndroid"><a href="#ActiveAndroid" class="headerlink" title="ActiveAndroid"></a>ActiveAndroid</h2><p>ActiveAndroid项目地址:<a href="https://github.com/pardom/ActiveAndroid" target="_blank" rel="external">https://github.com/pardom/ActiveAndroid</a></p>
<p>使用ActiveAndroid，需要在AndroidManifest中添加meta信息,用注解生成模型<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">&lt;meta-data android:name=<span class="string">"AA_DB_NAME"</span> android:value=<span class="string">"my_database.db"</span> /&gt;</span><br><span class="line">&lt;meta-data android:name=<span class="string">"AA_DB_VERSION"</span> android:value=<span class="string">"1"</span> /&gt;</span><br></pre></td></tr></table></figure></p>
<p>然后需要在Application中使用 ActiveAndroid.initialize(this);初始化<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">class</span> <span class="title">MyApplication</span> <span class="keyword">extends</span> <span class="title">SomeLibraryApplication</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">onCreate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate();</span><br><span class="line">        ActiveAndroid.initialize(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>CRUD:<br>通过类Select(查询),Delete(删),bean.save()增改,其wiki介绍也十分详细</p>
<h2 id="Realm"><a href="#Realm" class="headerlink" title="Realm"></a>Realm</h2><p>一个不依赖于SqlLite的ORM库,底层使用c++ 实现的,是最新流行的Orm框架,而且是跨平台的.官网地址:<a href="https://realm.io" target="_blank" rel="external">https://realm.io</a></p>
<p>启用步骤:第一步,使用gradle来引入realm-android<br>第二步,新建JavaBean实现RealmModel接口.<br>第三步,CRUD操作,官网介绍也十分详细.</p>
<p>普通操作:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Obtain a Realm instance</span></span><br><span class="line">Realm realm = Realm.getDefaultInstance();</span><br><span class="line"></span><br><span class="line">realm.beginTransaction();</span><br><span class="line"></span><br><span class="line"><span class="comment">//... add or update objects here ...</span></span><br><span class="line">User user = realm.createObject(User.class);</span><br><span class="line"></span><br><span class="line">realm.commitTransaction();</span><br><span class="line"><span class="comment">//or cancel</span></span><br><span class="line"><span class="comment">//realm.cancelTransaction();</span></span><br></pre></td></tr></table></figure>
<p>relam支持链式查询和Auto-Updating Results</p>
<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">RealmResults&lt;User&gt; teenagers = realm.where(User.class).between(<span class="string">"age"</span>, <span class="number">13</span>, <span class="number">20</span>).findAll();</span><br><span class="line">User firstJohn = teenagers.where().equalTo(<span class="string">"name"</span>, <span class="string">"John"</span>).findFirst();</span><br></pre></td></tr></table></figure>
<p>需要注意的是,在使用完毕后需要关闭 Realm 实例。</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></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">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Realm realm;</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">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Looper.prepare();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            realm = Realm.getDefaultInstance();</span><br><span class="line">            <span class="comment">//... Setup the handlers using the Realm instance ...</span></span><br><span class="line">            Lopper.loop();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (realm != <span class="keyword">null</span>) &#123;</span><br><span class="line">                realm.close();</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>使用Relam还有一个好处就是可以结合当下比较流行的Rxjava和Retrofit来使用,<br>其原生提供了很好的支持,截止目前已经出了1.0.0版本,但是仍有诸多限制,及JavaBean的不自由.</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;在android平台上，常用的数据保存方式有Preference，文件和数据库，针对于数据库操作，&lt;br&gt;一般都会采用orm框架来解决问题，Android中使用比较广泛的Orm有LiteOrm,OrmLite，SugarORM，GreenDAO，ActiveAndroid，Realm等&lt;/p&gt;
&lt;p&gt;AndroidOrm使用的小demo:&lt;a href=&quot;https://github.com/BoBoMEe/AndroidDev/tree/orm&quot;&gt;AndroidOrm&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="http://bobomee.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="orm" scheme="http://bobomee.github.io/tags/orm/"/>
    
  </entry>
  
  <entry>
    <title>Java依赖注入（控制反转）</title>
    <link href="http://bobomee.github.io/2015/12/30/ioc_di/"/>
    <id>http://bobomee.github.io/2015/12/30/ioc_di/</id>
    <published>2015-12-30T13:53:46.000Z</published>
    <updated>2017-05-14T11:09:20.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>名词释义：<br>依赖注入(Dependency Injection)，俗称DI。我们将互相协的关系称为依赖关系，在Java应用中，A对象调用了B对象的方法，我们可以说A依赖于B。<br>系统将实例创建出来，供调用着使用，就可以看作是系统将依赖注入了调用者。</p>
<p>控制反转(Inversion of Control),俗称IoC。控制反转就是关于一个对象如何获取他所依赖的对象的引用这个责任的反转。在Java中，就是不需要调用者new出来一个依赖对象，这个责任交给系统，在需要的时候直接使用系统提供的实例。</p>
<a id="more"></a>
<h2 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h2><p>优点：依赖注入在某种程度上实现了热插拔，降低了耦合度。<br>缺点：有些依赖注入框架使用的是反射，一定程度降低了效率。</p>
<h2 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</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></pre></td><td class="code"><pre><span class="line">classA</span><br><span class="line">&#123;</span><br><span class="line">    AInterface a;</span><br><span class="line"></span><br><span class="line">    A()&#123;&#125;</span><br><span class="line"></span><br><span class="line">    AMethod()<span class="comment">//一个方法</span></span><br><span class="line">    &#123;</span><br><span class="line">        a = <span class="keyword">new</span> AInterfaceImpl();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里面 Class A与AInterfaceImpl就是依赖关系,如果想使用AInterface的另外一个实现就需要更改代码了,依赖注入就是为了解决这种耦合关系的<br>使用new（对象创建）是一种硬编码,是代码耦合度变得很高,不方便测试.依赖注入简单的讲就是通过外界传入依赖来进行成员变量的初始化,比如可以采用如下方式</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></pre></td><td class="code"><pre><span class="line">classA</span><br><span class="line">&#123;</span><br><span class="line">    AInterface a;</span><br><span class="line"></span><br><span class="line">    A()&#123;&#125;</span><br><span class="line"></span><br><span class="line">    AMethod(AInterface a)<span class="comment">//一个方法</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">this</span>.a = a;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="依赖注入方式"><a href="#依赖注入方式" class="headerlink" title="依赖注入方式"></a>依赖注入方式</h2><p>依赖注入一般有如下几种方式</p>
<ul>
<li>Contructor Injection(构造函数注入)</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></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">IFather</span> </span>&#123;</span><br><span class="line"> <span class="comment">//method</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Human</span> </span>&#123;</span><br><span class="line">   IFather father;</span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="title">Human</span><span class="params">(IFather father)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">this</span>.father = father;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>基于setter,通过JavaBean的属性(setter方法)为可服务对象指定服务</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Human</span>  </span>&#123;</span><br><span class="line">IFather father;</span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setIFather</span><span class="params">(IFather father)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">this</span>.father = father;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>接口注入</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">// 注入功能的interface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">InjectFinder</span> </span>&#123;</span><br><span class="line">   <span class="function"><span class="keyword">void</span> <span class="title">injectFinder</span><span class="params">(IFather father)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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="comment">// 让我们的Human实现接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Human</span> <span class="keyword">implements</span> <span class="title">InjectFinder</span>   </span>&#123;</span><br><span class="line">IFather father;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">injectFinder</span><span class="params">(IFather father)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.father = father;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参考:<br><a href="https://github.com/android-cn/blog/tree/master/java/dependency-injection" target="_blank" rel="external">dependency-injection</a><br><a href="http://codethink.me/2015/08/01/dependency-injection-theory/" target="_blank" rel="external">dependency-injection-theory</a><br><a href="http://blog.csdn.net/yqj2065/article/details/8510074" target="_blank" rel="external">依赖注入（Dependency Injection）模式</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;名词释义：&lt;br&gt;依赖注入(Dependency Injection)，俗称DI。我们将互相协的关系称为依赖关系，在Java应用中，A对象调用了B对象的方法，我们可以说A依赖于B。&lt;br&gt;系统将实例创建出来，供调用着使用，就可以看作是系统将依赖注入了调用者。&lt;/p&gt;
&lt;p&gt;控制反转(Inversion of Control),俗称IoC。控制反转就是关于一个对象如何获取他所依赖的对象的引用这个责任的反转。在Java中，就是不需要调用者new出来一个依赖对象，这个责任交给系统，在需要的时候直接使用系统提供的实例。&lt;/p&gt;
    
    </summary>
    
      <category term="Java基础" scheme="http://bobomee.github.io/categories/Java%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="IOC" scheme="http://bobomee.github.io/tags/IOC/"/>
    
      <category term="DI" scheme="http://bobomee.github.io/tags/DI/"/>
    
  </entry>
  
</feed>
