1 Star 1 Fork 0

WAY / way

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
search.xml 497.83 KB
一键复制 编辑 原始数据 按行查看 历史
liweiping 提交于 2020-08-16 16:04 . Site updated: 2020-08-16 16:04:16
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>解耦神器Dagger2</title>
<link href="/2017/09/10/blogs/Dagger2/"/>
<url>/2017/09/10/blogs/Dagger2/</url>
<content type="html"><![CDATA[<h2 id="一-Dagger2介绍"><a href="#一-Dagger2介绍" class="headerlink" title="一. Dagger2介绍"></a>一. Dagger2介绍</h2><h3 id="1-Dagger2是什么?"><a href="#1-Dagger2是什么?" class="headerlink" title="1.Dagger2是什么?"></a>1.Dagger2是什么?</h3><p>&emsp;&emsp;Dagger2是由Google接手开发,最早的版本Dagger1 是由Square公司开发的,大神<a href="https://github.com/JakeWharton" target="_blank" rel="noopener">JakeWharton</a>最近也从 Square 公司跳槽到 Google。<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">A fast dependency injector <span class="keyword">for</span> Android and Java</span><br><span class="line">Android和Java的依赖快速注入器</span><br></pre></td></tr></table></figure></p><h3 id="2-Dagger2相较于Dagger1的优势是什么?"><a href="#2-Dagger2相较于Dagger1的优势是什么?" class="headerlink" title="2. Dagger2相较于Dagger1的优势是什么?"></a>2. Dagger2相较于Dagger1的优势是什么?</h3><ul><li><strong>更好的性能</strong>:相较于Dagger1,它使用的预编译期间生成代码来完成依赖注入,而不是用的反射。大家知道反射对手机应用开发影响是比较大的,因为反射是在程序运行时加载类来进行处理所以会比较耗时,而手机硬件资源有限,所以相对来说会对性能产生一定的影响。</li><li><strong>容易跟踪调试</strong>:因为Dagger2是使用生成代码来实现完整依赖注入,所以完全可以在相关代码处下断点进行运行调试。</li></ul><h3 id="3-使用依赖注入的最大好处是什么?"><a href="#3-使用依赖注入的最大好处是什么?" class="headerlink" title="3. 使用依赖注入的最大好处是什么?"></a>3. 使用依赖注入的最大好处是什么?</h3><p>&emsp;&emsp;快速自动的构建出我们所需要的依赖对象,这里的依赖对象可以理解为某一个成员变量。例如在 <code>MVP</code> 中,<code>VP</code> 层就是互相关联的, <code>V</code> 要依赖对应的 <code>P</code>,而 <code>P</code> 也要依赖对应的 <code>V</code> 。Dagger2 能解决的就是这种依赖关系,通过注入的方式,将双方的耦合再次降低,在实际的使用中体现为一个注解想要的对象就创建好了,咱们不用再去管理所依赖对象的创建等情况了。</p><h3 id="4-举个例子"><a href="#4-举个例子" class="headerlink" title="4. 举个例子"></a>4. 举个例子</h3><p>&emsp;&emsp;如果在 <code>MainActivity</code> 中,有 <code>Tinno</code>的实例,则称 <code>MainActivity</code> 对 <code>Tinno</code> 有一个依赖。如果不用Dagger2的情况下我们应该这么写:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Tinno mTinno;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">MainActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> mTinno = <span class="keyword">new</span> Tinno();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;上面例子面临着一个问题,一旦某一天<code>Tinno</code>的创建方式(如构造参数)发生改变,那么你不但需要修改<code>MainActivity</code>中创建<code>Tinno</code>的代码,还要修改其他所有地方创建<code>Tinno</code>的代码。如果我们使用了Dagger2的话,就不需要管这些了,只需要在需要<code>Tinno</code>的地方写下:<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="meta">@Inject</span></span><br><span class="line">Tinno mTinno;</span><br></pre></td></tr></table></figure></p><h2 id="二-Dagger2使用"><a href="#二-Dagger2使用" class="headerlink" title="二. Dagger2使用"></a>二. Dagger2使用</h2><h3 id="1-gradle配置"><a href="#1-gradle配置" class="headerlink" title="1. gradle配置"></a>1. gradle配置</h3><p>&emsp;&emsp;Android Studio 2.2以前的版本需要使用Gradle插件<code>android-apt</code>(Annotation Processing Tool),协助Android Studio处理<code>annotation processors</code>;<code>annotationProcessor</code>就是APT工具中的一种,是google开发的内置框架,不需要引入,所以可以像下面这样直接使用。<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Add Dagger dependencies</span></span><br><span class="line">dependencies &#123;</span><br><span class="line"> compile <span class="string">'com.google.dagger:dagger:2.4'</span></span><br><span class="line"> annotationProcessor <span class="string">'com.google.dagger:dagger-compiler:2.4'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="2-注解"><a href="#2-注解" class="headerlink" title="2. 注解"></a>2. 注解</h3><p>&emsp;&emsp;Dagger2 通过注解来生成代码,定义不同的角色,主要的注解如下:</p><ul><li><strong>@Module</strong>: 用来标注类,<code>Module</code>类里面的方法专门提供依赖,所以我们定义一个类,用<code>@Module</code>注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。</li><li><strong>@Provides</strong>: 用来标注方法,在<code>Module</code>中,我们定义的方法是用这个注解,以此来告诉<code>Dagger2</code>我们想要构造对象并提供这些依赖。</li><li><strong>@Inject</strong>: 用来标注对象变量或构造方法,通常在需要依赖的地方使用这个注解。换句话说,你用它告诉<code>Dagger2</code>这个类或者字段需要依赖注入。这样,<code>Dagger2</code>就会构造一个这个类的实例并满足他们的依赖。</li><li><strong>@Component</strong>: 通常用来标注接口,<code>Component</code>从根本上来说就是一个注入器,也可以说是<code>@Inject</code>和<code>@Module</code>的桥梁,它的主要作用就是连接这两个部分。将<code>Module</code>中产生的依赖对象自动注入到需要依赖实例的Container中。</li><li><strong>@Scope</strong>: 标注<code>Component</code>和<code>Module</code>提供对象的方法,<code>Dagger2</code>可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期。</li><li><strong>@Qualifier</strong>: 用来标注方法,当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的<code>Context</code>,所以我们就可以定义<code>Qualifier</code>注解<code>@ApplicationQualifier</code>和<code>@ActivityQualifier</code>,这样当注入一个<code>Context</code>的时候,我们就可以告诉<code>Dagger2</code>我们想要哪种类型的<code>Context</code>。</li></ul><h3 id="3-结构"><a href="#3-结构" class="headerlink" title="3. 结构"></a>3. 结构</h3><p>&emsp;&emsp;Dagger2要实现一个完整的依赖注入,通常必不可少的元素有三种:<strong>Module</strong>,<strong>Component</strong>,<strong>Container</strong>。为了便于理解,其实可以把<code>component</code>想象成<code>针管</code>,<code>module</code>是<code>注射器</code>,里面的<code>依赖对象</code>是<code>待注入的药水</code>,<code>build方法</code>是插进<code>患者(Container)</code>,<code>inject方法</code>的调用是<code>推动活塞</code>。</p><div align="center"><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/dagger2/1.png"></div><h3 id="4-简单的例子"><a href="#4-简单的例子" class="headerlink" title="4. 简单的例子"></a>4. 简单的例子</h3><p><strong>声明需要依赖的对象</strong>:使用了注解方式,还是以<code>Tinno</code>为例,使得Dagger2能找到它。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tinno</span> </span>&#123;</span><br><span class="line"> <span class="meta">@Inject</span> <span class="comment">//这里可以看到加入了注解方式</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Tinno</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> <strong>声明<code>Component</code>接口</strong>:声明完后rebuild一下工程,使其自动生成<code>Component</code>实现类<code>DaggerMainActivityComponent</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//用这个标注标识是一个连接器</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainActivityComponent</span> </span>&#123;</span><br><span class="line"> <span class="comment">//这个连接器要注入的对象。这个inject标注的意思是,我后面的参数对象里面有标注为@Inject的属性,这个标注的属性是需要这个连接器注入进来的。</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity activity)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> <strong>在使用的地方注入,这里是 MainActivity:</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></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 class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"MainActivity"</span>;</span><br><span class="line"> <span class="meta">@Inject</span></span><br><span class="line"> Tinno mTinno;<span class="comment">//加入注解,标注这个Tinno是需要注入的</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"> TextView dagger2TextView = (TextView) findViewById(R.id.dagger2_text_view);</span><br><span class="line"> <span class="comment">//使用组件进行构造,注入</span></span><br><span class="line"> DaggerMainActivityComponent.builder().build().inject(<span class="keyword">this</span>);</span><br><span class="line"> Log.d(TAG, <span class="string">"onCreate: mTinno = "</span> + mTinno);</span><br><span class="line"></span><br><span class="line"> dagger2TextView.setText(mTinno.toString());</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp; 这是最简单的一种使用了。首先我们看到,第一印象是我去😲,这个更复杂了啊😂😂。我只能说确实,因为这个是它对的最基础的使用,看起来很笨拙,但是当它在大型项目里面,在依赖更多的情况下,则会发生质的飞跃,会发现它非常好用,并且将你需要传递的参数都隐藏掉,来实现解耦。</p><h3 id="5-常规使用方法"><a href="#5-常规使用方法" class="headerlink" title="5. 常规使用方法"></a>5. 常规使用方法</h3><p>&emsp;&emsp;细心的朋友发现了,我在结构中说 Dagger2结构的时候提到通常必不可少的三元素,这个例子只用到了 <strong>Component</strong>和<strong>Container</strong>,而 <strong>Module</strong> 并未提及,通过以下这个例子,能更加深刻的理解 <strong>Module</strong> 的作用。<br>&emsp;&emsp;实现一个 <strong>MainModule</strong>,提供一些实例构造,通过 <strong>Component</strong> 联系起来。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Module</span> <span class="comment">//实现一个类,标注为 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Provides</span> <span class="comment">//实现一些提供方法,供外部使用</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Tinno <span class="title">provideTinno</span><span class="params">()</span></span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tinno();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;在 <strong>MainComponent</strong> 中,指明 <strong>Component</strong> 查找 <strong>Module</strong> 的位置<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span>(modules = MainModule.class)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainActivityComponent</span> </span>&#123;<span class="comment">// 通常定义为接口,Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××,这里对应的实现类是DaggerMainActivityComponent</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity activity)</span></span>;<span class="comment">// 注入到MainActivity(Container)的方法,方法名一般使用inject</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;最后我们的<code>Tinno</code>类中的<code>@Inject</code>和构造函数可以去掉了(亲测不去掉也是可以正常运行的,此时也是使用<code>Module</code>中提供的对象,具体可以通过后面分享的<code>@Scope</code>来验证,这样说明:<strong>Component会首先从Module维度中查找类实例,若找到就用Module维度创建类实例,并停止查找Inject维度,否则才是从Inject维度查找类实例,所以创建类实例级别Module维度要高于Inject维度。</strong>),如下所示。<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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tinno</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;注入使用的地方完全不用修改,也能得到和之前例子一样的结果。</p><h3 id="6-更多用法"><a href="#6-更多用法" class="headerlink" title="6. 更多用法"></a>6. 更多用法</h3><h4 id="6-1-方法参数"><a href="#6-1-方法参数" class="headerlink" title="6.1 方法参数"></a>6.1 方法参数</h4><p>&emsp;&emsp;上面的例子<code>@Provides</code>标注的方法是没有输入参数的,<code>Module</code>中<code>@Provides</code>标注的方法是可以带输入参数的,其参数值可以由<code>Module</code>中的其他被<code>@Provides</code>标注的方法提供。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Module</span> <span class="comment">//实现一个类,标注为 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> Context mContext;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MainModule</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line"> mContext = context;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Provides</span> <span class="comment">//实现一些提供方法,供外部使用</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Tinno <span class="title">provideTinno</span><span class="params">(Gson gson, CameraTeam cameraTeam)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tinno(mContext, gson, cameraTeam);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Gson <span class="title">provideGson</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> GsonBuilder()</span><br><span class="line"> .excludeFieldsWithModifiers(Modifier.PROTECTED)<span class="comment">//忽略protected字段</span></span><br><span class="line"> .setDateFormat(<span class="string">"yyyy-MM-dd'T'HH:mm:ssZ"</span>)</span><br><span class="line"> .create();</span><br><span class="line"></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// @Provides</span></span><br><span class="line"><span class="comment">// public CameraTeam provideCameraTeam() &#123;</span></span><br><span class="line"><span class="comment">// return new CameraTeam();</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;如果找不到被<code>@Provides</code>注释的方法提供对应参数对象的话,将会自动调用被<code>@Inject</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></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">CameraTeam</span> </span>&#123;</span><br><span class="line"> <span class="meta">@Inject</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">CameraTeam</span><span class="params">()</span> </span>&#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>&emsp;&emsp;由于我们修改了<code>MainModule</code>,所以对应注入的地方要稍微修改一下:<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">//注意此处比之前多了.mainModule(new MainModule(getApplicationContext()))</span></span><br><span class="line">DaggerMainActivityComponent.builder().mainModule(<span class="keyword">new</span> MainModule(getApplicationContext())).build().inject(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure></p><p><strong>思考</strong><br>&emsp;&emsp; 通过上面3个例子<code>@Provides</code>和<code>@Inject</code>两种方式提供对象的区别?</p><h4 id="6-2-添加多个Module"><a href="#6-2-添加多个Module" class="headerlink" title="6.2 添加多个Module"></a>6.2 添加多个Module</h4><p>&emsp;&emsp;一个<code>Component</code>可以添加多个<code>Module</code>,这样<code>Component</code>获取依赖时候会自动从多个<code>Module</code>中查找获取。添加多个<code>Module</code>有两种方法,一种是在<code>Component</code>的注解<code>@Component(modules={××××,×××})</code>中添加多个<code>modules</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span>(modules=&#123;MainModule.class,ModuleA.class,ModuleB.class,ModuleC.class&#125;) <span class="comment">//直接在Component引用多个 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainActivityComponent</span> </span>&#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;另外一种添加多个<code>Module</code>的方法可以使用<code>@Module</code>的 <code>includes</code>的方法<code>(includes={××××,×××})</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Module</span>(includes=&#123;ModuleA.class,ModuleB.class,ModuleC.class&#125;)<span class="comment">//先在一个 Module 中includes其他 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Component</span>(modules=&#123;MainModule.class&#125;) <span class="comment">//只有一个 Module 时可以不用&#123;&#125;</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainActivityComponent</span> </span>&#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="6-3-区分返回类型相同-Provides方法"><a href="#6-3-区分返回类型相同-Provides方法" class="headerlink" title="6.3 区分返回类型相同@Provides方法"></a>6.3 区分返回类型相同@Provides方法</h4><p>&emsp;&emsp;如果我们在 <code>Module</code> 中有重复的类型返回,例如我定义两个 <code>Context</code> 类型的<code>Provides</code> 在 <code>Module</code> 中的话,编译直接会报错:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Error:(16, 10) 错误: android.content.Context is bound multiple times:</span><br><span class="line">@Provides android.content.Context com.ape.dagger2.MainModule.provideApplicationContext()</span><br><span class="line">@Provides android.content.Context com.ape.dagger2.MainModule.provideActivityContext()</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;那如果我们真的需要注入同一类型多次呢,这个问题总会有解决方案的吧?要是真的这么坑估计也没人用 Dagger2 了吧!哈哈。。。😂 其实 Dagger2 为我们提供了两种方式来解决这个问题:</p><ul><li><strong>可以使用<code>@Qualifier</code> 的注解来区分</strong></li><li><strong><code>@Named(&quot;xx&quot;)</code> 的注解</strong>。</li></ul><p><strong>@Named 方式</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Module</span> <span class="comment">//实现一个类,标注为 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> Context mApplicationContext;</span><br><span class="line"> <span class="keyword">private</span> Context mActivityContext;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MainModule</span><span class="params">(Context context, Context activityContext)</span> </span>&#123;</span><br><span class="line"> mApplicationContext = context;</span><br><span class="line"> mActivityContext = activityContext;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Provides</span> <span class="comment">//实现一些提供方法,供外部使用</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Tinno <span class="title">provideTinno</span><span class="params">(@Named(<span class="string">"application"</span>)</span><span class="comment">/*使用的是 application*/</span>Context context, Gson gson, CameraTeam cameraTeam) </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tinno(context, gson, cameraTeam);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Named</span>(<span class="string">"application"</span>) <span class="comment">//标注为 application</span></span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Context <span class="title">provideApplicationContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mApplicationContext;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Named</span>(<span class="string">"activity"</span>) <span class="comment">//标注为 activity</span></span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Context <span class="title">provideActivityContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mActivityContext;</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><strong>@Qualifier方式</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Qualifier</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Retention</span>(RUNTIME)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> ApplicationQualifier &#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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Qualifier</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Retention</span>(RUNTIME)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> ActivityQualifier &#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><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><br><span class="line"><span class="meta">@Module</span> <span class="comment">//实现一个类,标注为 Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> Context mApplicationContext;</span><br><span class="line"> <span class="keyword">private</span> Context mActivityContext;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MainModule</span><span class="params">(Context context, Context activityContext)</span> </span>&#123;</span><br><span class="line"> mApplicationContext = context;</span><br><span class="line"> mActivityContext = activityContext;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Provides</span> <span class="comment">//实现一些提供方法,供外部使用</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Tinno <span class="title">provideTinno</span><span class="params">(@ApplicationQualifier<span class="comment">/*此处使用为 ApplicationQualifier*/</span> Context context, Gson gson, CameraTeam cameraTeam)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tinno(context, gson, cameraTeam);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@ApplicationQualifier</span> <span class="comment">//标注为 ApplicationQualifier</span></span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Context <span class="title">provideApplicationContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mApplicationContext;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@ActivityQualifier</span> <span class="comment">//标注为 ActivityQualifier</span></span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Context <span class="title">provideActivityContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mActivityContext;</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>&emsp;&emsp;使用哪种方式就仁者见仁智者见智了,但个人推荐使用<code>@Qualifier</code>,毕竟输入太多字符串容易出错。</p><h4 id="6-4-组件间依赖和子组件"><a href="#6-4-组件间依赖和子组件" class="headerlink" title="6.4 组件间依赖和子组件"></a>6.4 组件间依赖和子组件</h4><p>&emsp;&emsp;有时我们需要依赖一个组件,这个最常见的用法是,如果我们定义了 <code>MainActivity</code> 的 <code>MainComponent</code> ,并且它依赖咱们的 <code>AppComponent</code> 里面的 <code>IRepositoryManager</code> 的话就要这样定义了:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Component</span>(dependencies = AppComponent.class, modules = MainPresenterModule.class)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainComponent</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity activity)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;在 <code>AppComponent</code> 中需要将获取 <code>IRepositoryManager</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><br><span class="line"><span class="meta">@Component</span>(modules = &#123;AppModule.class&#125;)</span><br><span class="line"><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 class="comment">//用于管理网络请求层,以及数据缓存层,对外开放的接口</span></span><br><span class="line"> <span class="function">IRepositoryManager <span class="title">repositoryManager</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;那如果我觉得暴露这些方法太麻烦了,那需要怎么办呢?最简单就是使用 <code>@SubComponent</code> ,在所属的父 <code>Component</code> 中定义一个 <code>SubComponent</code>,该 <code>SubComponent</code> 中将会包含父 <code>Component</code> 的所有方法,父 <code>Component</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><br><span class="line"><span class="meta">@Subcomponent</span>(modules = MainPresenterModule.class)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainComponent</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity activity)</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Component</span>(modules = &#123;AppModule.class&#125;)</span><br><span class="line"><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 class="comment">// 提供 MainComponent 对象的获取方法</span></span><br><span class="line"> <span class="function">MainComponent <span class="title">mainComponent</span><span class="params">(MainPresenterModule <span class="keyword">module</span>)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;在注入的时候直接使用父组件的<code>mainComponent(MainPresenterModule module)</code>包含子组件的<code>module</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><br><span class="line">appComponent.mainComponent(<span class="keyword">new</span> MainPresenterModule(<span class="keyword">this</span>)).inject(<span class="keyword">this</span>);</span><br><span class="line"><span class="comment">//DaggerMainComponent.builder().appComponent(appComponent)</span></span><br><span class="line"><span class="comment">// .mainPresenterModule(new MainPresenterModule(this)).build().inject(this);</span></span><br></pre></td></tr></table></figure><p><strong>组件依赖和子组件的区别</strong>:</p><table><thead><tr><th>组件依赖</th><th>子组件</th></tr></thead><tbody><tr><td>1. 保持两个 Component 都独立,没有任何关联<br>2. 明确的告诉别人这个 Component 所依赖的 Component <br>3. 两个拥有依赖关系的 Component 是不能有相同 @Scope 注解的<br>4. 依赖的组件会生成Dagger…Component</td><td>1. 保持两个 Component 内聚<br>2. 不关心这个 Component 依赖哪个 Component<br>3. 可以使用相同的@Scope注解<br>4. 子组件的组件不会生成Dagger…Component</td></tr></tbody></table><h4 id="6-5-懒加载和强加载模式"><a href="#6-5-懒加载和强加载模式" class="headerlink" title="6.5 懒加载和强加载模式"></a>6.5 懒加载和强加载模式</h4><p>&emsp;&emsp;在上面的比喻中,一针扎进去,是啥都给你打进去了,那么如果有些我想要在调用的时候才加载呢?这里 Dagger2 提供了 <code>Lazy&lt;T&gt;</code> 的方式来注入;同时相反的提供一个强制加载方式<code>Provider&lt;T&gt;</code>,每次调用get都会调用Module的Provides方法一次,对应的获取就是:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Container</span></span>&#123;</span><br><span class="line"> <span class="meta">@Inject</span> Lazy&lt;Tinno&gt; mTinnoLazy; <span class="comment">//延迟加载</span></span><br><span class="line"> <span class="meta">@Inject</span> Provider&lt;Tinno&gt; mTinnoProvider;<span class="comment">//实现强制加载,每次调用get都会调用Module的Provides方法一次,和懒加载模式正好相反,比如我们需要一次性创建出10个Tinno 对象</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span>&#123;</span><br><span class="line"> DaggerComponent.create().inject(<span class="keyword">this</span>);</span><br><span class="line"> Tinno tinno = mTinnoLazy.get(); <span class="comment">//调用get时才创建b</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="6-6-Scope详解"><a href="#6-6-Scope详解" class="headerlink" title="6.6 @Scope详解"></a>6.6 <code>@Scope</code>详解</h4><p><strong>@Scope 是什么</strong><br>&emsp;&emsp; <code>Scope</code> 翻译过来就是辖域,再结合到计算机上,其实就是作用域的意思,学过高级语言的应该都知道设计模式中一个模式叫做单例模式,单例即为全局中该对象的实例只存在一个,而在 Dagger2 中,<code>@scope</code> 的一个默认实现就是 <code>@Singleton</code>,也是Dagger2唯一自带的Scope注解,下面是<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Scope</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Retention</span>(RUNTIME)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Singleton&#123;&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到定义一个<code>Scope</code>注解,通常需要添加以下三部分:</p><ul><li><strong>@Scope</strong>:注明是Scope</li><li><strong>@Documented</strong>:标记文档提示,可以不用</li><li><strong>@Retention(RUNTIME)</strong>:运行时级别</li></ul><p><strong>@Scpoe 怎么用</strong><br>&emsp;&emsp; 那么接下来我们就看一下它的使用。代码如下:<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></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tinno</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span><span class="comment">//声明Module</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainModule</span> </span>&#123;</span><br><span class="line"> <span class="meta">@Provides</span></span><br><span class="line"> <span class="meta">@Singleton</span></span><br><span class="line"> <span class="function">Tinno <span class="title">provideTinno</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tinno();</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">@Singleton</span></span><br><span class="line"><span class="meta">@Component</span>(modules = UserModule.class)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MainActivityComponent</span> </span>&#123;<span class="comment">//同一个Component可以声明多个注入Container</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(MainActivity activity)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">inject</span><span class="params">(SecondActivity activity)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp; 我们创建一个普通的 <code>Tinno</code> 类,然后创建它的<code>Module</code>,并且用 <code>@Singleton</code> 标记该 <code>Tinno</code> 返回对象,最后我们再创建它的 <code>Component</code>,然后用 <code>@Singleton</code> 标记这个 <code>Component</code>。这是一个标准的套路流程。接下来我们创建一个 <code>MainActivity</code> 和一个 <code>SecondActivity</code>,代码如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></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 class="meta">@Inject</span></span><br><span class="line"> Tinno mTinno1;</span><br><span class="line"> <span class="meta">@Inject</span></span><br><span class="line"> Tinno mTinno2;</span><br><span class="line"> <span class="keyword">private</span> TextView mContentTextView;</span><br><span class="line"> <span class="keyword">private</span> Button mContentButton;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"> mContentTextView = (TextView) findViewById(R.id.tv_content);</span><br><span class="line"> mContentButton = (Button) findViewById(R.id.btn_content);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// [1]</span></span><br><span class="line"> MainActivityComponent component = DaggerMainActivityComponent.create();</span><br><span class="line"> component.inject(<span class="keyword">this</span>);</span><br><span class="line"> <span class="comment">// 第一行为 mTinno1 的信息,第二行为 mTinno2 的信息,第三行为该类中 MainActivityComponent 的信息</span></span><br><span class="line"> mContentTextView.setText(mTinno1.toString() + <span class="string">"\n"</span> + mTinno2.toString()+<span class="string">"\n"</span>+ component.toString());</span><br><span class="line"> mContentButton.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>&#123;</span><br><span class="line"> startActivity(<span class="keyword">new</span> Intent(MainActivity.<span class="keyword">this</span>, SecondActivity.class));</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SecondActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line"> <span class="meta">@Inject</span></span><br><span class="line"> Tinno mTinno;</span><br><span class="line"> <span class="keyword">private</span> TextView mContentTextView;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_second);</span><br><span class="line"> mContentTextView = (TextView) findViewById(R.id.tv_content);</span><br><span class="line"> <span class="comment">// [2]</span></span><br><span class="line"> MainActivityComponent component = DaggerMainActivityComponent.create();</span><br><span class="line"> component.inject(<span class="keyword">this</span>);</span><br><span class="line"> <span class="comment">// 第一行为 mTinno 的信息,第二行为该类中 MainActivityComponent 的信息</span></span><br><span class="line"> mContentTextView.setText(mTinno.toString() + <span class="string">"\n"</span> + component.toString());</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>运行结果如下图所示,没有问题的,单例实现成功了,发现两个 <code>Tinno</code>的地址是一样的。</p><div align="center"><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/dagger2/2.png"></div><p>我们仅仅通过一个 <code>@Singleton</code> 标记就使得对象实现了单例模式,接下来我们点一下按钮跳转到 <code>SecondActivity</code> 中,如下图所示:</p><div align="center"><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/dagger2/3.png"></div><p>&emsp;&emsp;但是此时我们发现,不对啊,<code>SecondActivity</code> 的 <code>Tinno</code> 对象的地址和 <code>MainActivity</code> 中的 <code>Tinno</code> 对象地址并不一样啊,这个单例好像失效了啊!事实上并不是这样,那么为什么这个单例“失效”了呢?细心的小伙伴们已经看到了,两个 <code>Activity</code> 中的 <code>Component</code> 对象的地址是并不一样的,这样就好理解了 ——— 由于 <code>Component</code> 对象不是同一个,当然它们注入的对象也不会是同一个。那么我们如何解决这个问题呢?<br>我们在 <code>Application</code> 层初始化 <code>MainActivityComponent</code>,然后在 <code>Activity</code> 中直接获取这个 <code>MainActivityComponent</code> 对象,由于 <code>Application</code> 在全局中只会初始化一次, 所以 <code>Application</code> 中的 <code>MainActivityComponent</code> 对象只初始化一次,我们每次在 <code>Activity</code> 中获取 <code>Application</code> 中的这个 <code>MainActivityComponent</code> 当然就是同一个的啦。<code>Application</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></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">App</span> <span class="keyword">extends</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line"> MainActivityComponent mComponent;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onCreate();</span><br><span class="line"> mComponent = DaggerMainActivityComponent.create();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> MainActivityComponent <span class="title">getComponent</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mComponent;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>&emsp;&emsp;我们只需要将 [1] 和 [2] 处的代码更改成 <code>MainActivityComponent component = ((App)getApplication()).getComponent();</code>这样我们不就能将我们的 <code>MainActivityComponent</code> “单例”化了吗?截图这里就不再贴出了。</p><p><strong>自定义@Scpoe</strong></p><p>&emsp;&emsp;Dagger2中<code>@Singleton</code>和自己定义的<code>@ActivityScope</code>、<code>@ApplicationScope</code>等代码上并没有什么区别,区别是在那种<code>Component</code>依赖的<code>Component</code>的情况下,两个<code>Component</code>的<code>@Scope</code>不能相同,既然没什么区别,那为什么还要这么做呢?是因为这样标示可以清晰的区分<code>Component</code>依赖的层次,方便理清我们的代码逻辑层次,如下为自定义的<code>ActivityScope</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Scope</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Retention</span>(RUNTIME)</span><br><span class="line"><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></p><p>&emsp;&emsp;有<code>@Scope</code>注解和没<code>@Scope</code>注解的编译时生成代码的区别,在编译生成的<code>DaggerMainActivityComponent</code>的<code>initialize</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="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initialize</span><span class="params">(<span class="keyword">final</span> Builder builder)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//标记了`@Singleton`中`DaggerMainActivityComponent`中实例化provideTinnoProvider方式</span></span><br><span class="line"> <span class="keyword">this</span>.provideTinnoProvider =</span><br><span class="line"> DoubleCheck.provider(MainModule_ProvideTinnoFactory.create(builder.mainModule));</span><br><span class="line"> <span class="comment">//未标记</span></span><br><span class="line"> <span class="keyword">this</span>.provideTinnoProvider = MainModule_ProvideTinnoFactory.create(builder.mainModule);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;有<code>@Scope</code>类注解的<code>@Provider</code>生成的代码,外层多了一层<code>DoubleCheck.provider(…);</code>没有<code>@Scope</code>类注解的则是直接create一个新的实例。关于<code>DoubleCheck</code>,简单来说就是加了<code>@Scope</code>的<code>Provider</code>,<code>Dagger</code>会缓存一个实例在<code>DaggerMainComponent</code>中,在<code>DaggerMainComponent</code>中保持单例,缓存的<code>provide</code>跟随<code>DaggerMainComponent</code>的生命周期,<code>DaggerMainComponent</code>被销毁时,<code>provider</code>也被销毁,这就是局部单例的概念,假如你的<code>DaggerMainComponent</code>是在你应用的<code>application</code>中,则就形成了全局单例。</p><h2 id="三-小结"><a href="#三-小结" class="headerlink" title="三. 小结"></a>三. 小结</h2><h3 id="1-Dagger2到底有哪些好处?"><a href="#1-Dagger2到底有哪些好处?" class="headerlink" title="1. Dagger2到底有哪些好处?"></a>1. Dagger2到底有哪些好处?</h3><ul><li><p><strong>增加开发效率、省去重复的简单体力劳动</strong><br>首先new一个实例的过程是一个重复的简单体力劳动,Dagger2完全可以把new一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。省去写单例的方法,并且也不需要担心自己写的单例方法是否线程安全,自己写的单例是懒汉模式还是饿汉模式。因为Dagger2都可以把这些工作做了。</p></li><li><p><strong>更好的管理类实例</strong><br>每个app中的ApplicationComponent管理整个app的全局类实例,所有的全局类实例都统一交给ApplicationComponent管理,并且它们的生命周期与app的生命周期一样。每个页面对应自己的Component,页面Component管理着自己页面所依赖的所有类实例。因为Component,Module,整个app的类实例结构变的很清晰。</p></li><li><p><strong>解耦</strong><br>假如不用Dagger2的话,一个类的new代码是非常可能充斥在app的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。设计模式中提倡把容易变化的部分封装起来。</p></li></ul><h3 id="2-Dagger2在Camera中使用思考!"><a href="#2-Dagger2在Camera中使用思考!" class="headerlink" title="2. Dagger2在Camera中使用思考!"></a>2. Dagger2在Camera中使用思考!</h3><ul><li><strong>CameraScheduler</strong> 中依赖的各个模块实例可以通过Dagger2注入,回调接口也可以在注入的时候传递过去,类似与MVP模式将V传递给P。</li><li>各个子模块也可以使用Dagger2来注入相关实例。</li></ul><p><strong>本文所演示的代码在此下载</strong>:<a href="https://github.com/way1989/Dagger2Test" target="_blank" rel="noopener">Dagger2Sample</a></p><p><strong>MVP使用 Dagger2的例子在此下载</strong>:<a href="https://github.com/way1989/MaterialWeather" target="_blank" rel="noopener">MaterialWeather</a></p>]]></content>
<categories>
<category> Dagger2 </category>
</categories>
<tags>
<tag> Dagger2 </tag>
</tags>
</entry>
<entry>
<title>Android开发各国语言后缀对照</title>
<link href="/2017/06/07/notes/AndroidLanguage/"/>
<url>/2017/06/07/notes/AndroidLanguage/</url>
<content type="html"><![CDATA[<h2 id="android工程的对应关系"><a href="#android工程的对应关系" class="headerlink" title="android工程的对应关系"></a>android工程的对应关系</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line">国家 | 后缀 </span><br><span class="line">中文(中国)|values-zh-rCN</span><br><span class="line"></span><br><span class="line">中文(台湾)|values-zh-rTW</span><br><span class="line"></span><br><span class="line">中文(香港)|values-zh-rHK</span><br><span class="line"></span><br><span class="line">英语(美国)|values-en-rUS</span><br><span class="line"></span><br><span class="line">英语(英国)|values-en-rGB</span><br><span class="line"></span><br><span class="line">英文(澳大利亚)|values-en-rAU</span><br><span class="line"></span><br><span class="line">英文(加拿大)|values-en-rCA</span><br><span class="line"></span><br><span class="line">英文(爱尔兰)|values-en-rIE</span><br><span class="line"></span><br><span class="line">英文(印度)|values-en-rIN</span><br><span class="line"></span><br><span class="line">英文(新西兰)|values-en-rNZ</span><br><span class="line"></span><br><span class="line">英文(新加坡)|values-en-rSG</span><br><span class="line"></span><br><span class="line">英文(南非)|values-en-rZA</span><br><span class="line"></span><br><span class="line">阿拉伯文(埃及)|values-ar-rEG</span><br><span class="line"></span><br><span class="line">阿拉伯文(以色列)|values-ar-rIL</span><br><span class="line"></span><br><span class="line">保加利亚文: values-bg-rBG</span><br><span class="line"></span><br><span class="line">加泰罗尼亚文|values-ca-rES</span><br><span class="line"></span><br><span class="line">捷克文|values-cs-rCZ</span><br><span class="line"></span><br><span class="line">丹麦文|values-da-rDK</span><br><span class="line"></span><br><span class="line">德文(奥地利)|values-de-rAT</span><br><span class="line"></span><br><span class="line">德文(瑞士)|values-de-rCH</span><br><span class="line"></span><br><span class="line">德文(德国)|values-de-rDE</span><br><span class="line"></span><br><span class="line">德文(列支敦士登)|values-de-rLI</span><br><span class="line"></span><br><span class="line">希腊文|values-el-rGR</span><br><span class="line"></span><br><span class="line">西班牙文(西班牙)|values-es-rES</span><br><span class="line"></span><br><span class="line">西班牙文(美国)|values-es-rUS</span><br><span class="line"></span><br><span class="line">芬兰文(芬兰)|values-fi-rFI</span><br><span class="line"></span><br><span class="line">法文(比利时)|values-fr-rBE</span><br><span class="line"></span><br><span class="line">法文(加拿大)|values-fr-rCA</span><br><span class="line"></span><br><span class="line">法文(瑞士)|values-fr-rCH</span><br><span class="line"></span><br><span class="line">法文(法国)|values-fr-rFR</span><br><span class="line"></span><br><span class="line">希伯来文|values-iw-rIL</span><br><span class="line"></span><br><span class="line">印地文|values-hi-rIN</span><br><span class="line"></span><br><span class="line">克罗里亚文|values-hr-rHR</span><br><span class="line"></span><br><span class="line">匈牙利文|values-hu-rHU</span><br><span class="line"></span><br><span class="line">印度尼西亚文|values-in-rID</span><br><span class="line"></span><br><span class="line">意大利文(瑞士)|values-it-rCH</span><br><span class="line"></span><br><span class="line">意大利文(意大利)|values-it-rIT</span><br><span class="line"></span><br><span class="line">日文|values-ja-rJP</span><br><span class="line"></span><br><span class="line">韩文|values-ko-rKR</span><br><span class="line"></span><br><span class="line">立陶宛文|valueslt-rLT</span><br><span class="line"></span><br><span class="line">拉脱维亚文|values-lv-rLV</span><br><span class="line"></span><br><span class="line">挪威博克马尔文|values-nb-rNO</span><br><span class="line"></span><br><span class="line">荷兰文(比利时)|values-nl-BE</span><br><span class="line"></span><br><span class="line">荷兰文(荷兰)|values-nl-rNL</span><br><span class="line"></span><br><span class="line">波兰文|values-pl-rPL</span><br><span class="line"></span><br><span class="line">葡萄牙文(巴西)|values-pt-rBR</span><br><span class="line"></span><br><span class="line">葡萄牙文(葡萄牙)|values-pt-rPT</span><br><span class="line"></span><br><span class="line">罗马尼亚文|values-ro-rRO</span><br><span class="line"></span><br><span class="line">俄文|values-ru-rRU</span><br><span class="line"></span><br><span class="line">斯洛伐克文|values-sk-rSK</span><br><span class="line"></span><br><span class="line">斯洛文尼亚文|values-sl-rSI</span><br><span class="line"></span><br><span class="line">塞尔维亚文|values-sr-rRS</span><br><span class="line"></span><br><span class="line">瑞典文|values-sv-rSE</span><br><span class="line"></span><br><span class="line">泰文|values-th-rTH</span><br><span class="line"></span><br><span class="line">塔加洛语|values-tl-rPH</span><br><span class="line"></span><br><span class="line">土耳其文|values--r-rTR</span><br><span class="line"></span><br><span class="line">乌克兰文|values-uk-rUA</span><br><span class="line"></span><br><span class="line">越南文|values-vi-rVN</span><br><span class="line"></span><br><span class="line">缅甸语 | values-my(补充一种新语言)</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> Android </category>
</categories>
<tags>
<tag> Language </tag>
</tags>
</entry>
<entry>
<title>Camera2录像流程</title>
<link href="/2017/01/18/blogs/Camera2/"/>
<url>/2017/01/18/blogs/Camera2/</url>
<content type="html"><![CDATA[<h2 id="Android-M版本及以上需要申请权限"><a href="#Android-M版本及以上需要申请权限" class="headerlink" title="Android M版本及以上需要申请权限"></a>Android M版本及以上需要申请权限</h2><p> 申请<code>CAMERA</code>和<code>RECORD_AUDIO</code>权限。</p><h2 id="在TextureView可用的时候,打开Camera,通过CameraDevice-StateCallback回调获取打开结果。"><a href="#在TextureView可用的时候,打开Camera,通过CameraDevice-StateCallback回调获取打开结果。" class="headerlink" title="在TextureView可用的时候,打开Camera,通过CameraDevice.StateCallback回调获取打开结果。"></a>在TextureView可用的时候,打开Camera,通过<code>CameraDevice.StateCallback</code>回调获取打开结果。</h2><p> 其中openCamera函数声明如下(<code>cameraId</code>:Camera的id,通常0表示后置摄像头,1表示前置摄像头;<code>callback</code>: <code>CameraDevice.StateCallback</code>相机状态回调; <code>handler</code>:执行回调操作的hanlder,为空时,在主线程中执行,如果传入异步线程的handler,则在异步线程中执行):</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequiresPermission</span>(android.Manifest.permission.CAMERA)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">openCamera</span><span class="params">(@NonNull String cameraId,</span></span></span><br><span class="line"><span class="function"><span class="params"> @NonNull <span class="keyword">final</span> CameraDevice.StateCallback callback, @Nullable Handler handler)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> CameraAccessException </span>&#123;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p> 完整打开Camera代码如下:</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></span><br><span class="line"><span class="comment"> * Tries to open a &#123;<span class="doctag">@link</span> CameraDevice&#125;. The result is listened by `mStateCallback`.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">openCamera</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (!hasPermissionsGranted(VIDEO_PERMISSIONS)) &#123;</span><br><span class="line"> requestVideoPermissions();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">final</span> Activity activity = getActivity();</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> == activity || activity.isFinishing()) &#123;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//获取CameraManager对象</span></span><br><span class="line"> CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> Log.d(TAG, <span class="string">"tryAcquire"</span>);</span><br><span class="line"> <span class="comment">//做打开超时判断,超过2.5秒则抛出异常,对应release()结束</span></span><br><span class="line"> <span class="keyword">if</span> (!mCameraOpenCloseLock.tryAcquire(<span class="number">2500</span>, TimeUnit.MILLISECONDS)) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Time out waiting to lock camera opening."</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> String cameraId = manager.getCameraIdList()[<span class="number">0</span>];<span class="comment">//获取后摄id</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// Choose the sizes for camera preview and video recording</span></span><br><span class="line"> CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);</span><br><span class="line"> <span class="comment">//类似之前的Parameters</span></span><br><span class="line"> StreamConfigurationMap map = characteristics</span><br><span class="line"> .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);</span><br><span class="line"> mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);</span><br><span class="line"> <span class="comment">//选择录像尺寸</span></span><br><span class="line"> mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));</span><br><span class="line"> <span class="comment">//选择合适的预览尺寸</span></span><br><span class="line"> mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),</span><br><span class="line"> width, height, mVideoSize);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> orientation = getResources().getConfiguration().orientation;</span><br><span class="line"> <span class="keyword">if</span> (orientation == Configuration.ORIENTATION_LANDSCAPE) &#123;</span><br><span class="line"> mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());</span><br><span class="line"> &#125;</span><br><span class="line"> configureTransform(width, height);</span><br><span class="line"> mMediaRecorder = <span class="keyword">new</span> MediaRecorder();</span><br><span class="line"> <span class="comment">//打开Camera,通过CameraDevice.StateCallback回调打开结果</span></span><br><span class="line"> manager.openCamera(cameraId, mStateCallback, <span class="keyword">null</span>);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (CameraAccessException e) &#123;</span><br><span class="line"> Toast.makeText(activity, <span class="string">"Cannot access the camera."</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> activity.finish();</span><br><span class="line"> &#125; <span class="keyword">catch</span> (NullPointerException e) &#123;</span><br><span class="line"> <span class="comment">// Currently an NPE is thrown when the Camera2API is used but not supported on the</span></span><br><span class="line"> <span class="comment">// device this code runs.</span></span><br><span class="line"> ErrorDialog.newInstance(getString(R.string.camera_error))</span><br><span class="line"> .show(getChildFragmentManager(), FRAGMENT_DIALOG);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Interrupted while trying to lock camera opening."</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="调用openCamera方法后会回调CameraDevice-StateCallback这个接口,在该接口里重写onOpened函数,开启预览。"><a href="#调用openCamera方法后会回调CameraDevice-StateCallback这个接口,在该接口里重写onOpened函数,开启预览。" class="headerlink" title="调用openCamera方法后会回调CameraDevice.StateCallback这个接口,在该接口里重写onOpened函数,开启预览。"></a>调用<code>openCamera</code>方法后会回调<code>CameraDevice.StateCallback</code>这个接口,在该接口里重写<code>onOpened</code>函数,开启预览。</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> CameraDevice.StateCallback&#125; is called when &#123;<span class="doctag">@link</span> CameraDevice&#125; changes its status.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> CameraDevice.StateCallback mStateCallback = <span class="keyword">new</span> CameraDevice.StateCallback() &#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">onOpened</span><span class="params">(CameraDevice cameraDevice)</span> </span>&#123;<span class="comment">//打开Camera成功</span></span><br><span class="line"> mCameraDevice = cameraDevice;</span><br><span class="line"> startPreview();<span class="comment">//开始预览</span></span><br><span class="line"> mCameraOpenCloseLock.release();<span class="comment">//释放掉超时判断</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> != mTextureView) &#123;</span><br><span class="line"> configureTransform(mTextureView.getWidth(), mTextureView.getHeight());</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDisconnected</span><span class="params">(CameraDevice cameraDevice)</span> </span>&#123;<span class="comment">//Camera断开连接</span></span><br><span class="line"> mCameraOpenCloseLock.release();</span><br><span class="line"> cameraDevice.close();</span><br><span class="line"> mCameraDevice = <span class="keyword">null</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onError</span><span class="params">(CameraDevice cameraDevice, <span class="keyword">int</span> error)</span> </span>&#123;<span class="comment">//打开Camera失败</span></span><br><span class="line"> mCameraOpenCloseLock.release();</span><br><span class="line"> cameraDevice.close();</span><br><span class="line"> mCameraDevice = <span class="keyword">null</span>;</span><br><span class="line"> Activity activity = getActivity();</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> != activity) &#123;</span><br><span class="line"> activity.finish();</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><h2 id="开启预览主要就是使用CameraDevice来创建会话createCaptureSession。"><a href="#开启预览主要就是使用CameraDevice来创建会话createCaptureSession。" class="headerlink" title="开启预览主要就是使用CameraDevice来创建会话createCaptureSession。"></a>开启预览主要就是使用<code>CameraDevice</code>来创建会话<code>createCaptureSession</code>。</h2><p> 声明如下(<code>outputs</code>:预览输出载体,比如TextureView的getSurfaceTexture(),拍照时ImageReader的getSurface(),录像时MediaRecorder的getSurface();<code>callback</code>:摄像头采集状态回调;<code>handler</code>:同openCamera):</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="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">createCaptureSession</span><span class="params">(@NonNull List&lt;Surface&gt; outputs,</span></span></span><br><span class="line"><span class="function"><span class="params"> @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> CameraAccessException</span>;</span><br></pre></td></tr></table></figure><p>完整的startPreview函数如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Start the camera preview.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">startPreview</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> == mCameraDevice || !mTextureView.isAvailable() || <span class="keyword">null</span> == mPreviewSize) &#123;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> closePreviewSession();</span><br><span class="line"> SurfaceTexture texture = mTextureView.getSurfaceTexture();<span class="comment">//获取TextureView的SurfaceTexture,作为预览输出载体</span></span><br><span class="line"> <span class="keyword">assert</span> texture != <span class="keyword">null</span>;</span><br><span class="line"> texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());</span><br><span class="line"> <span class="comment">//创建CameraRequest.Builder, 当程序调用setRepeatingRequest()方法进行预览时,</span></span><br><span class="line"> <span class="comment">// 或调用capture()方法进行拍照时,都需要传入CameraRequest参数.</span></span><br><span class="line"> <span class="comment">// CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,</span></span><br><span class="line"> <span class="comment">// 比如对焦模式、曝光模式……程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。</span></span><br><span class="line"> <span class="comment">// 可以理解一个请求参数一样,CameraRequest.Builder则负责生成CameraRequest对象。</span></span><br><span class="line"> mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);</span><br><span class="line"></span><br><span class="line"> Surface previewSurface = <span class="keyword">new</span> Surface(texture);</span><br><span class="line"> mPreviewBuilder.addTarget(previewSurface);</span><br><span class="line"></span><br><span class="line"> mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), <span class="keyword">new</span> CameraCaptureSession.StateCallback() &#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">onConfigured</span><span class="params">(CameraCaptureSession cameraCaptureSession)</span> </span>&#123;</span><br><span class="line"> mPreviewSession = cameraCaptureSession;</span><br><span class="line"> mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);</span><br><span class="line"> <span class="comment">//不停的发送获取图像请求,完成连续预览</span></span><br><span class="line"> mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), <span class="keyword">null</span>, mBackgroundHandler);</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">onConfigureFailed</span><span class="params">(CameraCaptureSession cameraCaptureSession)</span> </span>&#123;</span><br><span class="line"> Activity activity = getActivity();</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> != activity) &#123;</span><br><span class="line"> Toast.makeText(activity, <span class="string">"Failed"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;, mBackgroundHandler);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (CameraAccessException e) &#123;</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></p><p> setRepeatingRequest和capture方法其实都是向相机设备发送获取图像的请求,但是capture就获取那么一次,而setRepeatingRequest就是不停的获取图像数据,所以呢,使用capture就像拍照一样,图像就停在那里,但是setRepeatingRequest一直在发送和获取,所以需要连拍的时候就调用它,然后在onCaptureCompleted中保存图像就行了。(注意了,图像的预览也是用的setRepeatingRequest,只是不处理数据)</p><h2 id="至此,Camera已经完成预览,等待用户输入开始录像事件。"><a href="#至此,Camera已经完成预览,等待用户输入开始录像事件。" class="headerlink" title="至此,Camera已经完成预览,等待用户输入开始录像事件。"></a>至此,Camera已经完成预览,等待用户输入开始录像事件。</h2><p>当用户点击录像按钮时,先配置好<code>MediaRecorder</code>相关参数,然后重启预览,并在预览开始时调用<code>MediaRecorder.start()</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><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></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">startRecordingVideo</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> == mCameraDevice || !mTextureView.isAvailable() || <span class="keyword">null</span> == mPreviewSize) &#123;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> closePreviewSession();<span class="comment">//先关闭预览,因为需要添加一个预览输入载体到MediaRecorder.getSurface()</span></span><br><span class="line"> setUpMediaRecorder();<span class="comment">//设置MediaRecorder相关参数</span></span><br><span class="line"> SurfaceTexture texture = mTextureView.getSurfaceTexture();<span class="comment">//获取TextureView的输出载体</span></span><br><span class="line"> <span class="keyword">assert</span> texture != <span class="keyword">null</span>;</span><br><span class="line"> texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());</span><br><span class="line"> <span class="comment">//准备创建CaptureRequest</span></span><br><span class="line"> mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);</span><br><span class="line"> List&lt;Surface&gt; surfaces = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Set up Surface for the camera preview</span></span><br><span class="line"> Surface previewSurface = <span class="keyword">new</span> Surface(texture);</span><br><span class="line"> surfaces.add(previewSurface);</span><br><span class="line"> mPreviewBuilder.addTarget(previewSurface);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Set up Surface for the MediaRecorder</span></span><br><span class="line"> mRecorderSurface = mMediaRecorder.getSurface();<span class="comment">//获取MediaRecorder预览输出载体</span></span><br><span class="line"> surfaces.add(mRecorderSurface);</span><br><span class="line"> mPreviewBuilder.addTarget(mRecorderSurface);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Start a capture session</span></span><br><span class="line"> <span class="comment">// Once the session starts, we can update the UI and start recording</span></span><br><span class="line"> mCameraDevice.createCaptureSession(surfaces, <span class="keyword">new</span> CameraCaptureSession.StateCallback() &#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">onConfigured</span><span class="params">(@NonNull CameraCaptureSession cameraCaptureSession)</span> </span>&#123;</span><br><span class="line"> mPreviewSession = cameraCaptureSession;</span><br><span class="line"> mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);</span><br><span class="line"> <span class="comment">//不停的发送获取图像请求,完成连续预览</span></span><br><span class="line"> mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), <span class="keyword">null</span>, mBackgroundHandler);</span><br><span class="line"> getActivity().runOnUiThread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">// UI</span></span><br><span class="line"> mButtonVideo.setText(R.string.stop);</span><br><span class="line"> mIsRecordingVideo = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Start recording</span></span><br><span class="line"> mMediaRecorder.start();<span class="comment">//正式开始录像</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onConfigureFailed</span><span class="params">(@NonNull CameraCaptureSession cameraCaptureSession)</span> </span>&#123;</span><br><span class="line"> Activity activity = getActivity();</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> != activity) &#123;</span><br><span class="line"> Toast.makeText(activity, <span class="string">"Failed"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;, mBackgroundHandler);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (CameraAccessException e) &#123;</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">&#125;</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><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">stopRecordingVideo</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="comment">// UI</span></span><br><span class="line"> mIsRecordingVideo = <span class="keyword">false</span>;</span><br><span class="line"> mButtonVideo.setText(R.string.record);</span><br><span class="line"> <span class="comment">// Stop recording</span></span><br><span class="line"> mMediaRecorder.stop();</span><br><span class="line"> mMediaRecorder.reset();</span><br><span class="line"></span><br><span class="line"> Activity activity = getActivity();</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> != activity) &#123;</span><br><span class="line"> Toast.makeText(activity, <span class="string">"Video saved: "</span> + mNextVideoAbsolutePath,</span><br><span class="line"> Toast.LENGTH_SHORT).show();</span><br><span class="line"> Log.d(TAG, <span class="string">"Video saved: "</span> + mNextVideoAbsolutePath);</span><br><span class="line"> &#125;</span><br><span class="line"> mNextVideoAbsolutePath = <span class="keyword">null</span>;</span><br><span class="line"> startPreview();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 博客 </category>
<category> Camera2 </category>
</categories>
<tags>
<tag> 博客 </tag>
</tags>
</entry>
<entry>
<title>Android运行时资源覆盖</title>
<link href="/2017/01/16/blogs/RuntimeResourceOverlay/"/>
<url>/2017/01/16/blogs/RuntimeResourceOverlay/</url>
<content type="html"><![CDATA[<h2 id="什么是运行时资源覆盖"><a href="#什么是运行时资源覆盖" class="headerlink" title="什么是运行时资源覆盖"></a>什么是运行时资源覆盖</h2><p>无需修改原app的代码,无需重新编译,就能覆盖app内的部分资源,从而实现修改app的部分UI,后面简称RRO。</p><h2 id="RRO原理"><a href="#RRO原理" class="headerlink" title="RRO原理"></a>RRO原理</h2><ul><li><p>应用通过 getString/getDrawable去调用某个资源时,会将这个resources ID 作为参数传给 framework 层。同一名称但不同状态的 resources ID 是一样的,比如不同分辨率但名称相同的图片分别被放置在了drawable-hdpi/drawable-ldpi/drawable-mdpi下,但在编译时针对该图片生成的resources ID只有一个。</p></li><li><p>为了快速查找到指定的资源,Apk编译的时候会把Java文件里面的R.String.app_name替换成ox7f123456这种格式的值</p></li><li><p>每个apk里面都有一个文件(resources.arsc)记录着指定的resource_id对应的资源类型,如果是string类型,则记录的这个资源名称对应的所有语言的翻译,如果是drawable类型,则记录着哪些分辨率底下有这个资源。</p></li></ul><h2 id="Resources-arsc的结构-string"><a href="#Resources-arsc的结构-string" class="headerlink" title="Resources.arsc的结构 string"></a>Resources.arsc的结构 string</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/runtimeResourceOverlay/2017-01-16-23-12-24.jpg"></p><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/runtimeResourceOverlay/2017-01-16-23-12-40.jpg"></p><h2 id="Resources-arsc的结构-drawable"><a href="#Resources-arsc的结构-drawable" class="headerlink" title="Resources.arsc的结构 drawable"></a>Resources.arsc的结构 drawable</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/runtimeResourceOverlay/2017-01-16-23-13-30.jpg"></p><h2 id="RRO实现方法"><a href="#RRO实现方法" class="headerlink" title="RRO实现方法"></a>RRO实现方法</h2><ul><li>建立一个新的overlay apk项目,apk项目底下有 res文件夹,Android.mk、AndroidManifest.xml</li><li>修改AndroidManifest.xml 配置apk的包名、要覆盖的apk的包名</li><li>修改Android.mk 使得apk能集成到system/vendor/overlay/目录下</li><li>找到需要覆盖的资源名,在overlay apk的res指定目录下定义同名的资源</li></ul><h2 id="AndroidManifest-xml的写法"><a href="#AndroidManifest-xml的写法" class="headerlink" title="AndroidManifest.xml的写法"></a>AndroidManifest.xml的写法</h2><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">manifest</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span> </span></span><br><span class="line"><span class="tag"><span class="attr">package</span>=<span class="string">"com.android.gallery3d.overlay"</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="name">overlay</span> <span class="attr">android:targetPackage</span>=<span class="string">"com.android.gallery3d"</span> <span class="attr">android:priority</span>=<span class="string">"1"</span>/&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="Android-mk的写法及自动生成版本号的实现"><a href="#Android-mk的写法及自动生成版本号的实现" class="headerlink" title="Android.mk的写法及自动生成版本号的实现"></a>Android.mk的写法及自动生成版本号的实现</h2><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ifeq</span> (<span class="variable">$(<span class="built_in">strip</span> <span class="variable">$(MYOS_APE_GALLERY31_SUPPORT)</span>)</span>, yes)</span><br><span class="line"><span class="keyword">ifneq</span> (<span class="variable">$(<span class="built_in">strip</span> <span class="variable">$(MYOS_APE_GALLERY31_NAME)</span>)</span>,)</span><br><span class="line"><span class="variable">$(<span class="built_in">warning</span> --Android.mk--ApeGalleryOverlay31---------)</span></span><br><span class="line"></span><br><span class="line">LOCAL_PATH:= <span class="variable">$(<span class="built_in">call</span> my-<span class="built_in">dir</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(CLEAR_VARS)</span></span><br><span class="line"></span><br><span class="line">LOCAL_MODULE_TAGS := optional</span><br><span class="line">LOCAL_SRC_FILES := <span class="variable">$(<span class="built_in">call</span> all-java-files-under, res)</span></span><br><span class="line">LOCAL_PACKAGE_NAME := ApeGalleryOverlay31</span><br><span class="line">LOCAL_CERTIFICATE := platform</span><br><span class="line">LOCAL_MODULE_PATH := <span class="variable">$(TARGET_OUT)</span>/vendor/overlay</span><br><span class="line"></span><br><span class="line">version1 = 6</span><br><span class="line">version2 = 0</span><br><span class="line">version3 = <span class="variable">$(<span class="built_in">shell</span> cd <span class="variable">$(LOCAL_PATH)</span> &amp;&amp; git rev-list --count HEAD)</span></span><br><span class="line">build_date = <span class="variable">$(<span class="built_in">shell</span> date +%y%m%d)</span></span><br><span class="line">last_commit = <span class="variable">$(<span class="built_in">shell</span> cd <span class="variable">$(LOCAL_PATH)</span> &amp;&amp; git describe --always)</span></span><br><span class="line"></span><br><span class="line">version_code = <span class="variable">$(<span class="built_in">shell</span> expr <span class="variable">$(version1)</span> \* 1000000 + <span class="variable">$(version2)</span> \* 10000 + <span class="variable">$(version3)</span>)</span></span><br><span class="line">version_name := <span class="variable">$(version1)</span>.<span class="variable">$(version2)</span>.<span class="variable">$(version3)</span>.<span class="variable">$(build_date)</span>.<span class="variable">$(last_commit)</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(<span class="built_in">warning</span> --ApeGalleryOverlay31---<span class="variable">$(version_code)</span>---<span class="variable">$(version_name)</span>---)</span></span><br><span class="line"></span><br><span class="line">LOCAL_AAPT_FLAGS += --version-code <span class="variable">$(version_code)</span></span><br><span class="line">LOCAL_AAPT_FLAGS += --version-name <span class="variable">$(version_name)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">include</span> <span class="variable">$(BUILD_PACKAGE)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">endif</span></span><br><span class="line"><span class="keyword">endif</span></span><br></pre></td></tr></table></figure><h2 id="自动生成版本号的意义"><a href="#自动生成版本号的意义" class="headerlink" title="自动生成版本号的意义"></a>自动生成版本号的意义</h2><ul><li>出现问题以后反查问题原因,是代码问题,还是集成问题</li><li>这里为什么要使用自动版本号:<ul><li>不用每次改版本号的麻烦</li><li>版本号有序自增,没有出错的风险</li><li>每一个编译出来的apk,都能定位到当时的最后一个提交</li><li>由于是随系统编译,可能你没来得及改版本号就编译了,所以手工改版本号可能只能定位到大致的位置</li></ul></li><li>注意事项:<ul><li>仓库提交9999次以后,版本会溢出,此时可以脚本向前进位</li></ul></li></ul><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ul><li>不能覆盖layout、AndroidManifest.xml、asset目录</li><li>建议只覆盖字符资源、覆盖图片要谨慎</li><li>Overlay apk中不要添加其它的东西,比方说style.xml 可能会导致被覆盖的apk打开就报错</li><li>Overlay apk 必须要签名,否则不生效 </li><li>为了安全起见,Overylay的apk必须放到system/vendor/overlay/目录下才会生效</li><li>由于是把源码放在基线里面编译,所以提交代码需要特别小心,不要造成项目编译不过</li></ul><h2 id="RRO对我们目前来说使用的意义"><a href="#RRO对我们目前来说使用的意义" class="headerlink" title="RRO对我们目前来说使用的意义"></a>RRO对我们目前来说使用的意义</h2><ul><li>对于字符翻译需求,快速输出版本</li><li>DCC以后,只有翻译需求,无需更新主apk版本</li></ul>]]></content>
<categories>
<category> 博客 </category>
</categories>
<tags>
<tag> 博客 </tag>
</tags>
</entry>
<entry>
<title>mac OS常用开发软件激活方式</title>
<link href="/2017/01/15/notes/MacOS/"/>
<url>/2017/01/15/notes/MacOS/</url>
<content type="html"><![CDATA[<h2 id="Beyond-Compare激活方法:"><a href="#Beyond-Compare激活方法:" class="headerlink" title="Beyond Compare激活方法:"></a>Beyond Compare激活方法:</h2><ul><li>去官网下载并安装:<a href="http://www.scootersoftware.com/download.php" target="_blank" rel="noopener">Beyond Compare</a></li><li>下载一个文件:<a href="http://o83jxl7u1.bkt.clouddn.com/trial.key" target="_blank" rel="noopener">trial.key</a></li><li>在finder 右击beyond文件&gt;显示包内容&gt;Contents&gt;Recourse 替换 trial.key</li><li>重启软件即可。</li></ul><h2 id="Adobe-Photoshop激活方法:"><a href="#Adobe-Photoshop激活方法:" class="headerlink" title="Adobe Photoshop激活方法:"></a>Adobe Photoshop激活方法:</h2><ul><li>下载一个文件后解压:<a href="http://o83jxl7u1.bkt.clouddn.com/amtlib.framework.zip" target="_blank" rel="noopener">amtlib.framework</a></li><li>安装完成后运行软件,选择“试用“,要断网,点击“稍后连接“,然后退出。</li><li>将破解包解压后,右键程序图标–&gt;显示包内容–&gt;contents–&gt;frameworks,用解压来的文件替换amtlib.framework。</li><li>重启软件,应该就没有倒计时了吧?</li></ul><h2 id="未完待续…"><a href="#未完待续…" class="headerlink" title="未完待续…"></a>未完待续…</h2>]]></content>
<categories>
<category> 笔记 </category>
<category> mac </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>各种常用脚本</title>
<link href="/2017/01/14/notes/Command/"/>
<url>/2017/01/14/notes/Command/</url>
<content type="html"><![CDATA[<h2 id="alogcat可以过滤打印出对应包名的log:"><a href="#alogcat可以过滤打印出对应包名的log:" class="headerlink" title="alogcat可以过滤打印出对应包名的log:"></a><code>alogcat</code>可以过滤打印出对应包名的log:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"># 作用:能够通过进程名显示log</span></span><br><span class="line"><span class="comment"># 用法:alogcat com.android.calendar or alogcat calendar</span></span><br><span class="line"><span class="comment"># 当监控的进程异常退出时,需要重新运行此命令</span></span><br><span class="line"><span class="comment">#function alogcat() &#123;</span></span><br><span class="line"> OUT=$(adb shell ps | grep -i <span class="variable">$1</span> | awk <span class="string">'&#123;print $2&#125;'</span>)</span><br><span class="line"> OUT=$(<span class="built_in">echo</span> <span class="variable">$OUT</span> | sed <span class="string">'s/[[:blank:]]\+/\|/g'</span>)</span><br><span class="line"> <span class="comment"># 当进程异常退出,log是通过 AndroidRuntime 输出的</span></span><br><span class="line"> adb logcat -v time | grep -E <span class="string">"<span class="variable">$OUT</span>|AndroidRuntime"</span></span><br><span class="line"><span class="comment">#&#125;</span></span><br></pre></td></tr></table></figure><h2 id="tingpng批量压缩图片资源的脚本:"><a href="#tingpng批量压缩图片资源的脚本:" class="headerlink" title="tingpng批量压缩图片资源的脚本:"></a><code>tingpng</code>批量压缩图片资源的脚本:</h2><figure class="highlight bash"><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">#!/bin/bash</span></span><br><span class="line">pngquant -f --skip-if-larger --ext .png --quality 50-80 `find . -name <span class="string">"*.png"</span> -<span class="built_in">type</span> f ! -name <span class="string">"*.9.png"</span>`</span><br></pre></td></tr></table></figure><h2 id="在Mac、Linux-终端显示-Git-当前所在分支"><a href="#在Mac、Linux-终端显示-Git-当前所在分支" class="headerlink" title="在Mac、Linux 终端显示 Git 当前所在分支"></a>在Mac、Linux 终端显示 Git 当前所在分支</h2><p>编辑.bashrc文件,将下面的代码加入到文件的最后处,保存退出,执行加载命令<code>source ./.bashrc</code>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">## Parses out the branch name from .git/HEAD:</span></span><br><span class="line"><span class="function"><span class="title">find_git_branch</span></span> () &#123;</span><br><span class="line"> <span class="built_in">local</span> dir=. head</span><br><span class="line"> until [ <span class="string">"<span class="variable">$dir</span>"</span> -ef / ]; <span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$dir</span>/.git/HEAD"</span> ]; <span class="keyword">then</span></span><br><span class="line"> head=$(&lt; <span class="string">"<span class="variable">$dir</span>/.git/HEAD"</span>)</span><br><span class="line"> <span class="keyword">if</span> [[ <span class="variable">$head</span> = ref:\ refs/heads/* ]]; <span class="keyword">then</span></span><br><span class="line"> git_branch=<span class="string">" → <span class="variable">$&#123;head#*/*/&#125;</span>"</span></span><br><span class="line"> <span class="keyword">elif</span> [[ <span class="variable">$head</span> != <span class="string">''</span> ]]; <span class="keyword">then</span></span><br><span class="line"> git_branch=<span class="string">" → (detached)"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> git_branch=<span class="string">" → (unknow)"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">return</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> dir=<span class="string">"../<span class="variable">$dir</span>"</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> git_branch=<span class="string">''</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">PROMPT_COMMAND=<span class="string">"find_git_branch; <span class="variable">$PROMPT_COMMAND</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Here is bash color codes you can use</span></span><br><span class="line"> black=$<span class="string">'\[\e[1;30m\]'</span></span><br><span class="line"> red=$<span class="string">'\[\e[1;31m\]'</span></span><br><span class="line"> green=$<span class="string">'\[\e[1;32m\]'</span></span><br><span class="line"> yellow=$<span class="string">'\[\e[1;33m\]'</span></span><br><span class="line"> blue=$<span class="string">'\[\e[1;34m\]'</span></span><br><span class="line">magenta=$<span class="string">'\[\e[1;35m\]'</span></span><br><span class="line"> cyan=$<span class="string">'\[\e[1;36m\]'</span></span><br><span class="line"> white=$<span class="string">'\[\e[1;37m\]'</span></span><br><span class="line"> normal=$<span class="string">'\[\e[m\]'</span></span><br><span class="line"></span><br><span class="line">PS1=<span class="string">"<span class="variable">$white</span>[<span class="variable">$white</span>\u<span class="variable">$white</span>@<span class="variable">$white</span>\h<span class="variable">$white</span>:<span class="variable">$white</span>\w<span class="variable">$yellow</span>\$git_branch<span class="variable">$white</span>]\$ <span class="variable">$normal</span>"</span></span><br></pre></td></tr></table></figure><h2 id="通过git-diffall-branch-a-branch-b来对比两个分支间差异配置:"><a href="#通过git-diffall-branch-a-branch-b来对比两个分支间差异配置:" class="headerlink" title="通过git diffall branch_a...branch_b来对比两个分支间差异配置:"></a>通过<code>git diffall branch_a...branch_b</code>来对比两个分支间差异配置:</h2><p>在~/bin目录下新建git-diffall文件:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment"># Copyright 2010 - 2012, Tim Henigan &lt;tim.henigan@gmail.com&gt;</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Permission is hereby granted, free of charge, to any person obtaining</span></span><br><span class="line"><span class="comment"># a copy of this software and associated documentation files (the</span></span><br><span class="line"><span class="comment"># "Software"), to deal in the Software without restriction, including</span></span><br><span class="line"><span class="comment"># without limitation the rights to use, copy, modify, merge, publish,</span></span><br><span class="line"><span class="comment"># distribute, sublicense, and/or sell copies of the Software, and to</span></span><br><span class="line"><span class="comment"># permit persons to whom the Software is furnished to do so, subject to</span></span><br><span class="line"><span class="comment"># the following conditions:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># The above copyright notice and this permission notice shall be included</span></span><br><span class="line"><span class="comment"># in all copies or substantial portions of the Software.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS</span></span><br><span class="line"><span class="comment"># OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF</span></span><br><span class="line"><span class="comment"># MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.</span></span><br><span class="line"><span class="comment"># IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY</span></span><br><span class="line"><span class="comment"># CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,</span></span><br><span class="line"><span class="comment"># TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE</span></span><br><span class="line"><span class="comment"># SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Perform a directory diff between commits in the repository using</span></span><br><span class="line"><span class="comment"># the external diff or merge tool specified in the user's config.</span></span><br><span class="line"></span><br><span class="line">USAGE=<span class="string">'[--cached] [--copy-back] [-x|--extcmd=&lt;command&gt;] &lt;commit&gt;&#123;0,2&#125; [-- &lt;path&gt;*]</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> --cached Compare to the index rather than the working tree.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> --copy-back Copy files back to the working tree when the diff</span></span><br><span class="line"><span class="string"> tool exits (in case they were modified by the</span></span><br><span class="line"><span class="string"> user). This option is only valid if the diff</span></span><br><span class="line"><span class="string"> compared with the working tree.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> -x=&lt;command&gt;</span></span><br><span class="line"><span class="string"> --extcmd=&lt;command&gt; Specify a custom command for viewing diffs.</span></span><br><span class="line"><span class="string"> git-diffall ignores the configured defaults and</span></span><br><span class="line"><span class="string"> runs $command $LOCAL $REMOTE when this option is</span></span><br><span class="line"><span class="string"> specified. Additionally, $BASE is set in the</span></span><br><span class="line"><span class="string"> environment.</span></span><br><span class="line"><span class="string">'</span></span><br><span class="line"></span><br><span class="line">SUBDIRECTORY_OK=1</span><br><span class="line">. <span class="string">"<span class="variable">$(git --exec-path)</span>/git-sh-setup"</span></span><br><span class="line"></span><br><span class="line">TOOL_MODE=diff</span><br><span class="line">. <span class="string">"<span class="variable">$(git --exec-path)</span>/git-mergetool--lib"</span></span><br><span class="line"></span><br><span class="line">merge_tool=<span class="string">"<span class="variable">$(get_merge_tool)</span>"</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -z <span class="string">"<span class="variable">$merge_tool</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Error: Either the 'diff.tool' or 'merge.tool' option must be set."</span></span><br><span class="line">usage</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">start_dir=$(<span class="built_in">pwd</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># All the file paths returned by the diff command are relative to the root</span></span><br><span class="line"><span class="comment"># of the working copy. So if the script is called from a subdirectory, it</span></span><br><span class="line"><span class="comment"># must switch to the root of working copy before trying to use those paths.</span></span><br><span class="line">cdup=$(git rev-parse --show-cdup) &amp;&amp;</span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$cdup</span>"</span> || &#123;</span><br><span class="line"><span class="built_in">echo</span> &gt;&amp;2 <span class="string">"Cannot chdir to <span class="variable">$cdup</span>, the toplevel of the working tree"</span></span><br><span class="line"><span class="built_in">exit</span> 1</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># set up temp dir</span></span><br><span class="line">tmp=$(perl -e <span class="string">'use File::Temp qw(tempdir);</span></span><br><span class="line"><span class="string">$t=tempdir("/tmp/git-diffall.XXXXX") or exit(1);</span></span><br><span class="line"><span class="string">print $t'</span>) || <span class="built_in">exit</span> 1</span><br><span class="line"><span class="built_in">trap</span> <span class="string">'rm -rf "$tmp"'</span> EXIT</span><br><span class="line"></span><br><span class="line">left=</span><br><span class="line">right=</span><br><span class="line">paths=</span><br><span class="line">dashdash_seen=</span><br><span class="line">compare_staged=</span><br><span class="line">merge_base=</span><br><span class="line">left_dir=</span><br><span class="line">right_dir=</span><br><span class="line">diff_tool=</span><br><span class="line">copy_back=</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="built_in">test</span> <span class="variable">$#</span> != 0</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$1</span>"</span> <span class="keyword">in</span></span><br><span class="line">-h|--h|--he|--hel|--<span class="built_in">help</span>)</span><br><span class="line">usage</span><br><span class="line">;;</span><br><span class="line">--cached)</span><br><span class="line">compare_staged=1</span><br><span class="line">;;</span><br><span class="line">--copy-back)</span><br><span class="line">copy_back=1</span><br><span class="line">;;</span><br><span class="line">-x|--e|--ex|--ext|--extc|--extcm|--extcmd)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> <span class="variable">$#</span> = 1</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> You must specify the tool <span class="keyword">for</span> use with --extcmd</span><br><span class="line">usage</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">diff_tool=<span class="variable">$2</span></span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">;;</span><br><span class="line">--)</span><br><span class="line">dashdash_seen=1</span><br><span class="line">;;</span><br><span class="line">-*)</span><br><span class="line"><span class="built_in">echo</span> Invalid option: <span class="string">"<span class="variable">$1</span>"</span></span><br><span class="line">usage</span><br><span class="line">;;</span><br><span class="line">*)</span><br><span class="line"><span class="comment"># could be commit, commit range or path limiter</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$1</span>"</span> <span class="keyword">in</span></span><br><span class="line">*...*)</span><br><span class="line">left=<span class="variable">$&#123;1%...*&#125;</span></span><br><span class="line">right=<span class="variable">$&#123;1#*...&#125;</span></span><br><span class="line">merge_base=1</span><br><span class="line">;;</span><br><span class="line">*..*)</span><br><span class="line">left=<span class="variable">$&#123;1%..*&#125;</span></span><br><span class="line">right=<span class="variable">$&#123;1#*..&#125;</span></span><br><span class="line">;;</span><br><span class="line">*)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$dashdash_seen</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">paths=<span class="string">"<span class="variable">$paths</span><span class="variable">$1</span> "</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">test</span> -z <span class="string">"<span class="variable">$left</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">left=<span class="variable">$1</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">test</span> -z <span class="string">"<span class="variable">$right</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">right=<span class="variable">$1</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">paths=<span class="string">"<span class="variable">$paths</span><span class="variable">$1</span> "</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line">;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Determine the set of files which changed</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$left</span>"</span> &amp;&amp; <span class="built_in">test</span> -n <span class="string">"<span class="variable">$right</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">left_dir=<span class="string">"cmt-<span class="variable">$(git rev-parse --short $left)</span>"</span></span><br><span class="line">right_dir=<span class="string">"cmt-<span class="variable">$(git rev-parse --short $right)</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$compare_staged</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">usage</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$merge_base</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">git diff --name-only <span class="string">"<span class="variable">$left</span>"</span>...<span class="string">"<span class="variable">$right</span>"</span> -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">git diff --name-only <span class="string">"<span class="variable">$left</span>"</span> <span class="string">"<span class="variable">$right</span>"</span> -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$left</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">left_dir=<span class="string">"cmt-<span class="variable">$(git rev-parse --short $left)</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$compare_staged</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">right_dir=<span class="string">"staged"</span></span><br><span class="line">git diff --name-only --cached <span class="string">"<span class="variable">$left</span>"</span> -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">right_dir=<span class="string">"working_tree"</span></span><br><span class="line">git diff --name-only <span class="string">"<span class="variable">$left</span>"</span> -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">left_dir=<span class="string">"HEAD"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$compare_staged</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">right_dir=<span class="string">"staged"</span></span><br><span class="line">git diff --name-only --cached -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">right_dir=<span class="string">"working_tree"</span></span><br><span class="line">git diff --name-only -- <span class="variable">$paths</span> &gt;<span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Exit immediately if there are no diffs</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> ! -s <span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">exit</span> 0</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$copy_back</span>"</span> &amp;&amp; <span class="built_in">test</span> <span class="string">"<span class="variable">$right_dir</span>"</span> != <span class="string">"working_tree"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"--copy-back is only valid when diff includes the working tree."</span></span><br><span class="line"><span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the named tmp directories that will hold the files to be compared</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>"</span> <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Populate the tmp/right_dir directory with the files to be compared</span></span><br><span class="line"><span class="keyword">while</span> <span class="built_in">read</span> name</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$right</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">ls_list=$(git ls-tree <span class="variable">$right</span> <span class="string">"<span class="variable">$name</span>"</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$ls_list</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">git show <span class="string">"<span class="variable">$right</span>"</span>:<span class="string">"<span class="variable">$name</span>"</span> &gt;<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$name</span>"</span> || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$compare_staged</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">ls_list=$(git ls-files -- <span class="string">"<span class="variable">$name</span>"</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$ls_list</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">git show :<span class="string">"<span class="variable">$name</span>"</span> &gt;<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$name</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -e <span class="string">"<span class="variable">$name</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">cp <span class="string">"<span class="variable">$name</span>"</span> <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>/<span class="variable">$name</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span> &lt; <span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Populate the tmp/left_dir directory with the files to be compared</span></span><br><span class="line"><span class="keyword">while</span> <span class="built_in">read</span> name</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$left</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">ls_list=$(git ls-tree <span class="variable">$left</span> <span class="string">"<span class="variable">$name</span>"</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$ls_list</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">git show <span class="string">"<span class="variable">$left</span>"</span>:<span class="string">"<span class="variable">$name</span>"</span> &gt;<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$name</span>"</span> || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$compare_staged</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">ls_list=$(git ls-tree HEAD <span class="string">"<span class="variable">$name</span>"</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$ls_list</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">git show HEAD:<span class="string">"<span class="variable">$name</span>"</span> &gt;<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$name</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">mkdir -p <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$(dirname "$name")</span>"</span></span><br><span class="line">git show :<span class="string">"<span class="variable">$name</span>"</span> &gt;<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>/<span class="variable">$name</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span> &lt; <span class="string">"<span class="variable">$tmp</span>/filelist"</span></span><br><span class="line"></span><br><span class="line">LOCAL=<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$left_dir</span>"</span></span><br><span class="line">REMOTE=<span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$diff_tool</span>"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">export</span> BASE</span><br><span class="line"><span class="built_in">eval</span> <span class="variable">$diff_tool</span> <span class="string">'"$LOCAL"'</span> <span class="string">'"$REMOTE"'</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">run_merge_tool <span class="string">"<span class="variable">$merge_tool</span>"</span> <span class="literal">false</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Copy files back to the working dir, if requested</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -n <span class="string">"<span class="variable">$copy_back</span>"</span> &amp;&amp; <span class="built_in">test</span> <span class="string">"<span class="variable">$right_dir</span>"</span> = <span class="string">"working_tree"</span></span><br><span class="line"><span class="keyword">then</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$start_dir</span>"</span></span><br><span class="line">git_top_dir=$(git rev-parse --show-toplevel)</span><br><span class="line">find <span class="string">"<span class="variable">$tmp</span>/<span class="variable">$right_dir</span>"</span> -<span class="built_in">type</span> f |</span><br><span class="line"><span class="keyword">while</span> <span class="built_in">read</span> file</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">cp <span class="string">"<span class="variable">$file</span>"</span> <span class="string">"<span class="variable">$git_top_dir</span>/<span class="variable">$&#123;file#$tmp/$right_dir/&#125;</span>"</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>然后配置<code>~/.gitconfig</code>文件:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">[color]</span><br><span class="line">ui = auto</span><br><span class="line">[user]</span><br><span class="line">name = xxx</span><br><span class="line">email = xxx@xxx.xx</span><br><span class="line">[<span class="built_in">alias</span>]</span><br><span class="line">st = status</span><br><span class="line">co = checkout</span><br><span class="line">ci = commit</span><br><span class="line">br = branch</span><br><span class="line">last = <span class="built_in">log</span> -1</span><br><span class="line">di = diff</span><br><span class="line">lg = <span class="built_in">log</span> --color --graph --pretty=format:<span class="string">'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset'</span> --abbrev-commit</span><br><span class="line">unstage = reset HEAD</span><br><span class="line">chp = cherry-pick</span><br><span class="line">diffall = ~/bin/git-diffall</span><br><span class="line">[core]</span><br><span class="line">quotepath = <span class="literal">false</span></span><br><span class="line">editor = vim</span><br><span class="line">[<span class="built_in">help</span>]</span><br><span class="line">autocorrect = 1</span><br><span class="line">[credential]</span><br><span class="line">helper = cache --timeout=31536000</span><br><span class="line">[diff]</span><br><span class="line"> tool = bc3</span><br><span class="line">[difftool]</span><br><span class="line"> prompt = <span class="literal">false</span></span><br><span class="line">[merge]</span><br><span class="line"> tool = bcomp</span><br><span class="line">[mergetool]</span><br><span class="line"> prompt = <span class="literal">false</span></span><br><span class="line">[mergetool <span class="string">"bcomp"</span>]</span><br><span class="line"> cmd = \<span class="string">"/usr/local/bin/bcomp\" \"<span class="variable">$LOCAL</span>\" \"<span class="variable">$REMOTE</span>\" \"<span class="variable">$BASE</span>\" \"<span class="variable">$MERGED</span>\"</span></span><br></pre></td></tr></table></figure><h2 id="配置git显示当前分支,在-bashrc文件末尾加上:"><a href="#配置git显示当前分支,在-bashrc文件末尾加上:" class="headerlink" title="配置git显示当前分支,在~/.bashrc文件末尾加上:"></a>配置git显示当前分支,在~/.bashrc文件末尾加上:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">## Parses out the branch name from .git/HEAD:</span></span><br><span class="line"><span class="function"><span class="title">find_git_branch</span></span> () &#123;</span><br><span class="line"> <span class="built_in">local</span> dir=. head</span><br><span class="line"> until [ <span class="string">"<span class="variable">$dir</span>"</span> -ef / ]; <span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$dir</span>/.git/HEAD"</span> ]; <span class="keyword">then</span></span><br><span class="line"> head=$(&lt; <span class="string">"<span class="variable">$dir</span>/.git/HEAD"</span>)</span><br><span class="line"> <span class="keyword">if</span> [[ <span class="variable">$head</span> = ref:\ refs/heads/* ]]; <span class="keyword">then</span></span><br><span class="line"> git_branch=<span class="string">" → <span class="variable">$&#123;head#*/*/&#125;</span>"</span></span><br><span class="line"> <span class="keyword">elif</span> [[ <span class="variable">$head</span> != <span class="string">''</span> ]]; <span class="keyword">then</span></span><br><span class="line"> git_branch=<span class="string">" → (detached)"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> git_branch=<span class="string">" → (unknow)"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">return</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> dir=<span class="string">"../<span class="variable">$dir</span>"</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> git_branch=<span class="string">''</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">PROMPT_COMMAND=<span class="string">"find_git_branch; <span class="variable">$PROMPT_COMMAND</span>"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Here is bash color codes you can use</span></span><br><span class="line"> black=$<span class="string">'\[\e[1;30m\]'</span></span><br><span class="line"> red=$<span class="string">'\[\e[1;31m\]'</span></span><br><span class="line"> green=$<span class="string">'\[\e[1;32m\]'</span></span><br><span class="line"> yellow=$<span class="string">'\[\e[1;33m\]'</span></span><br><span class="line"> blue=$<span class="string">'\[\e[1;34m\]'</span></span><br><span class="line">magenta=$<span class="string">'\[\e[1;35m\]'</span></span><br><span class="line"> cyan=$<span class="string">'\[\e[1;36m\]'</span></span><br><span class="line"> white=$<span class="string">'\[\e[1;37m\]'</span></span><br><span class="line"> normal=$<span class="string">'\[\e[m\]'</span></span><br><span class="line"></span><br><span class="line">PS1=<span class="string">"<span class="variable">$white</span>[<span class="variable">$white</span>\u<span class="variable">$white</span>@<span class="variable">$white</span>\h<span class="variable">$white</span>:<span class="variable">$white</span>\w<span class="variable">$yellow</span>\$git_branch<span class="variable">$white</span>]\$ <span class="variable">$normal</span>"</span></span><br></pre></td></tr></table></figure><h2 id="未完待续…"><a href="#未完待续…" class="headerlink" title="未完待续…"></a>未完待续…</h2>]]></content>
<categories>
<category> 笔记 </category>
<category> command </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>Git进阶用法</title>
<link href="/2016/12/15/notes/GitAdvance/"/>
<url>/2016/12/15/notes/GitAdvance/</url>
<content type="html"><![CDATA[<h2 id="Git重要概念:"><a href="#Git重要概念:" class="headerlink" title="Git重要概念:"></a>Git重要概念:</h2><p>对于任何一个文件,在 Git 内都只有三种状态:已修改(modified)、已暂存(staged)、已提交(committed):</p><ul><li>已修改表示修改了某个文件,但还没有提交保存.</li><li>已暂存表示把已修改的文件放在下次提交时要保存的清单中.</li><li><p>已提交表示该文件已经被安全地保存在本地数据库中了.</p><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/1.png"></p></li></ul><h2 id="两种分支合并的方法-rebase-与-merge:"><a href="#两种分支合并的方法-rebase-与-merge:" class="headerlink" title="两种分支合并的方法,rebase 与 merge:"></a>两种分支合并的方法,rebase 与 merge:</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/2.png"></p><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/3.png"></p><h2 id="Rebase的原理"><a href="#Rebase的原理" class="headerlink" title="Rebase的原理:"></a>Rebase的原理:</h2><ul><li>先把本地的未push的提交生成一个个patch</li><li>然后移除本地未上传的提交</li><li>把服务器最新的提交全拉下来</li><li>按修改顺序,重新应用本地的提交生成的patch</li></ul><p>由此可见,rebase过程可能产生冲突,而且可能需要解决多次</p><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/4.png"></p><h2 id="删除一个未上传的提交"><a href="#删除一个未上传的提交" class="headerlink" title="删除一个未上传的提交"></a>删除一个未上传的提交</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/5.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/6.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/7.png"></p><p>注意点:</p><ul><li>Rebase时log显示是按修改的顺序显示的,和git log 显示刚好是相反的</li><li>Rebase完以后编辑器里面最少需要保留一行有效内容,否则不会进行rebase</li><li>适合某个commit,在走读以后发现是不需要修改的</li></ul><h2 id="修改非最后一个提交"><a href="#修改非最后一个提交" class="headerlink" title="修改非最后一个提交"></a>修改非最后一个提交</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/8.png"></p><p>注意点:</p><ul><li>这个方案适合走读发现的小问题修改,适合少量的代码修改。</li><li>大量的代码修改,建议把要修改的内容变成一个commit,然后合并到原来的commit</li></ul><h2 id="合并本地未上传的提交"><a href="#合并本地未上传的提交" class="headerlink" title="合并本地未上传的提交"></a>合并本地未上传的提交</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/9.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/10.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/11.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/12.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/13.png"></p><p>注意点:</p><ul><li>只能合并到未上传的commit</li><li>如果多个提交合并到一个提交也是同理的,把要合并的commit按顺序移动到待合并的commit后面,要合并的commit前面的pick都修改成f,然后保存退出</li><li>Rebase过程可能出现冲突,此时需要修改冲突然后执行”git add . “ 和”git rebase –continue”</li></ul><h2 id="已经commit未push的提交,不小心reset掉了,怎么找回来"><a href="#已经commit未push的提交,不小心reset掉了,怎么找回来" class="headerlink" title="已经commit未push的提交,不小心reset掉了,怎么找回来"></a>已经commit未push的提交,不小心reset掉了,怎么找回来</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/14.png"></p><p>操作步骤及注意事项:</p><ul><li>根据后面的commit_msg找到对应的commit</li><li>执行命令git cherry-pick commit_id</li><li>各种rebase操作错误的时候,也可以利用git reflog,找到rebase之前的commit,然后执行git reset –hard commit_id (这个会丢弃本地未commit的修改内容)</li></ul><h2 id="把自己的修改内容合并到别人的commit里面去了"><a href="#把自己的修改内容合并到别人的commit里面去了" class="headerlink" title="把自己的修改内容合并到别人的commit里面去了"></a>把自己的修改内容合并到别人的commit里面去了</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/15.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/16.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/17.png"></p><p>操作步骤及注意事项</p><ul><li>根据后面的commit_msg找到对应的comit</li><li>git reset –soft commit_id</li></ul><h2 id="从gerrit获取别人提交的commit"><a href="#从gerrit获取别人提交的commit" class="headerlink" title="从gerrit获取别人提交的commit"></a>从gerrit获取别人提交的commit</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/18.png"><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/gitAdvance/19.png"></p><p>操作步骤及注意事项</p><ul><li>从gerrit查看到要下载的commit的commit_id</li><li>执行命令git ls-remote | grep 66e1d6ec3597,获取要下载commit在gerrit的路径</li><li>执行命令 git pull –rebase origin refs/changes/06/3306/1, 将要下载的commit rebase到本地</li><li>要下载的commit 所依赖的其它commit都会同时下载下来,走读时可只下载最后的commit</li></ul><h2 id="常见错误"><a href="#常见错误" class="headerlink" title="常见错误"></a>常见错误</h2><ul><li>git pull –rebase,出现冲突时,解决后不知道执行git rebase –continue</li><li>git cherry-pick commit_id 出现冲突时,解决后不知道执行git cherry-pick –continue</li><li>出现冲突时,直接切到其它分支</li></ul>]]></content>
<categories>
<category> 笔记 </category>
<category> GIT </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>Android深入浅出 Retrofit</title>
<link href="/2016/09/29/blogs/Retrofit/"/>
<url>/2016/09/29/blogs/Retrofit/</url>
<content type="html"><![CDATA[<h3 id="本文转自微信-Bugly"><a href="#本文转自微信-Bugly" class="headerlink" title="本文转自微信: Bugly"></a>本文转自微信: <a href="http://mp.weixin.qq.com/s/5EmNqqX7rt1KT62KXNCq3A" target="_blank" rel="noopener">Bugly</a></h3><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/Retrofit/1.jpg"></p><p>Android 开发中,从原生的 HttpUrlConnection 到经典的 Apache 的 HttpClient,再到对前面这些网络基础框架的封装,比如 Volley、Async Http Client,Http 相关开源框架的选择还是很多的,其中由著名的 Square 公司开源的 Retrofit 更是以其简易的接口配置、强大的扩展支持、优雅的代码结构受到大家的追捧。也正是由于 Square 家的框架一如既往的简洁优雅,所以我一直在想,Square 公司是不是只招处女座的程序员?</p><h2 id="初识-Retrofit"><a href="#初识-Retrofit" class="headerlink" title="初识 Retrofit"></a>初识 Retrofit</h2><p>单从 Retrofit 这个单词,你似乎看不出它究竟是干嘛的,当然,我也看不出来 :)逃。。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Retrofitting refers to the addition of new technology or features to older systems.</span><br><span class="line">—From Wikipedia</span><br></pre></td></tr></table></figure></p><p>于是我们就明白了,冠以 Retrofit 这个名字的这个家伙,应该是某某某的 『Plus』 版本了。</p><h3 id="Retrofit-概览"><a href="#Retrofit-概览" class="headerlink" title="Retrofit 概览"></a>Retrofit 概览</h3><p>Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的。Retrofit 2.0 开始内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作,宛如古人的『你耕地来我织布』,小日子别提多幸福了。<br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/Retrofit/2.jpg"><br>我们的应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。<br>讲到这里,你就会发现所谓 Retrofit,其实就是 Retrofitting OkHttp 了。</p><h3 id="Hello-Retrofit"><a href="#Hello-Retrofit" class="headerlink" title="Hello Retrofit"></a>Hello Retrofit</h3><p>多说无益,不要来段代码陶醉一下。使用 Retrofit 非常简单,首先你需要在你的 build.gradle 中添加依赖:<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">compile <span class="string">'com.squareup.retrofit2:retrofit:2.0.2'</span></span><br></pre></td></tr></table></figure></p><p>你一定是想要访问 GitHub 的 api 对吧,那么我们就定义一个接口:<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">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></pre></td></tr></table></figure></p><p>接口当中的 listRepos 方法,就是我们想要访问的 api 了:<br><code>https://api.github.com/users/{user}/repos</code><br>其中,在发起请求时, {user} 会被替换为方法的第一个参数 user。<br>好,现在接口有了,我们要构造 Retrofit 了:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line"> .baseUrl(<span class="string">"https://api.github.com/"</span>)</span><br><span class="line"> .build();</span><br><span class="line"> </span><br><span class="line">GitHubService service = retrofit.create(GitHubService.class);</span><br></pre></td></tr></table></figure></p><p>这里的 service 就好比我们的快递哥,还是往返的那种哈~<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">Call&lt;List&lt;Repo&gt;&gt; repos = service.listRepos(<span class="string">"octocat"</span>);</span><br></pre></td></tr></table></figure></p><p>发请求的代码就像前面这一句,返回的 repos 其实并不是真正的数据结果,它更像一条指令,你可以在合适的时机去执行它:<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="comment">// 同步调用</span></span><br><span class="line">List&lt;Repo&gt; data = repos.execute(); </span><br><span class="line"> </span><br><span class="line"><span class="comment">// 异步调用</span></span><br><span class="line">repos.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"> List&lt;Repo&gt; data = response.body();</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"> t.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br></pre></td></tr></table></figure></p><p>啥感觉?有没有突然觉得请求接口就好像访问自家的方法一样简单?呐,前面我们看到的,就是 Retrofit 官方的 demo 了。你以为这就够了?噗~怎么可能。。</p><h3 id="Url-配置"><a href="#Url-配置" class="headerlink" title="Url 配置"></a>Url 配置</h3><p>Retrofit 支持的协议包括 GET/POST/PUT/DELETE/HEAD/PATCH,当然你也可以直接用 HTTP 来自定义请求。这些协议均以注解的形式进行配置,比如我们已经见过 GET 的用法:<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="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></pre></td></tr></table></figure></p><p>这些注解都有一个参数 value,用来配置其路径,比如示例中的 users/{user}/repos,我们还注意到在构造 Retrofit 之时我们还传入了一个 baseUrl(“<a href="https://api.github.com/&quot;),请求的完整" target="_blank" rel="noopener">https://api.github.com/&quot;),请求的完整</a> Url 就是通过 baseUrl 与注解的 value(下面称 “path“ ) 整合起来的,具体整合的规则如下:</p><ul><li>path 是绝对路径的形式:<br> path = “/apath”,baseUrl = “<a href="http://host:port/a/b&quot;" target="_blank" rel="noopener">http://host:port/a/b&quot;</a><br> Url = “<a href="http://host:port/apath&quot;" target="_blank" rel="noopener">http://host:port/apath&quot;</a></li><li>path 是相对路径,baseUrl 是目录形式:<br> path = “apath”,baseUrl = “<a href="http://host:port/a/b/&quot;" target="_blank" rel="noopener">http://host:port/a/b/&quot;</a><br> Url = “<a href="http://host:port/a/b/apath&quot;" target="_blank" rel="noopener">http://host:port/a/b/apath&quot;</a></li><li>path 是相对路径,baseUrl 是文件形式:<br> path = “apath”,baseUrl = “<a href="http://host:port/a/b&quot;" target="_blank" rel="noopener">http://host:port/a/b&quot;</a><br> Url = “<a href="http://host:port/a/apath&quot;" target="_blank" rel="noopener">http://host:port/a/apath&quot;</a></li><li>path 是完整的 Url:<br> path = “<a href="http://host:port/aa/apath&quot;,baseUrl" target="_blank" rel="noopener">http://host:port/aa/apath&quot;,baseUrl</a> = “<a href="http://host:port/a/b&quot;" target="_blank" rel="noopener">http://host:port/a/b&quot;</a><br> Url = “<a href="http://host:port/aa/apath&quot;" target="_blank" rel="noopener">http://host:port/aa/apath&quot;</a></li></ul><p>建议采用第二种方式来配置,并尽量使用同一种路径形式。如果你在代码里面混合采用了多种配置形式,恰好赶上你哪天头晕眼花,信不信分分钟写一堆 bug 啊哈哈。</p><h3 id="参数类型"><a href="#参数类型" class="headerlink" title="参数类型"></a>参数类型</h3><p>发请求时,需要传入参数,Retrofit 通过注解的形式令 Http 请求的参数变得更加直接,而且类型安全。</p><h4 id="Query-amp-QueryMap"><a href="#Query-amp-QueryMap" class="headerlink" title="Query &amp; QueryMap"></a>Query &amp; QueryMap</h4><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 class="string">"/list"</span>)</span><br><span class="line"><span class="function">Call&lt;ResponseBody&gt; <span class="title">list</span><span class="params">(@Query(<span class="string">"page"</span>)</span> <span class="keyword">int</span> page)</span>;</span><br></pre></td></tr></table></figure><p>Query 其实就是 Url 中 ‘?’ 后面的 key-value,比如:<br><code>http://www.println.net/?cate=android</code><br>这里的 cate=android 就是一个 Query,而我们在配置它的时候只需要在接口方法中增加一个参数,即可:<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="class"><span class="keyword">interface</span> <span class="title">PrintlnServer</span></span>&#123;</span><br><span class="line"> <span class="meta">@GET</span>(<span class="string">"/"</span>)</span><br><span class="line"> <span class="function">Call&lt;String&gt; <span class="title">cate</span><span class="params">(@Query(<span class="string">"cate"</span>)</span> String cate)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这时候你肯定想,如果我有很多个 Query,这么一个个写岂不是很累?而且根据不同的情况,有些字段可能不传,这与方法的参数要求显然也不相符。于是,打群架版本的 QueryMap 横空出世了,使用方法很简单,我就不多说了。</p><h4 id="Field-amp-FieldMap"><a href="#Field-amp-FieldMap" class="headerlink" title="Field &amp; FieldMap"></a>Field &amp; FieldMap</h4><p>其实我们用 POST 的场景相对较多,绝大多数的服务端接口都需要做加密、鉴权和校验,GET 显然不能很好的满足这个需求。使用 POST 提交表单的场景就更是刚需了,怎么提呢?<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FormUrlEncoded</span></span><br><span class="line"><span class="meta">@POST</span>(<span class="string">"/"</span>)</span><br><span class="line"><span class="function">Call&lt;ResponseBody&gt; <span class="title">example</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params"> @Field(<span class="string">"name"</span>)</span> String name,</span></span><br><span class="line"><span class="function"> @<span class="title">Field</span><span class="params">(<span class="string">"occupation"</span>)</span> String occupation)</span>;</span><br></pre></td></tr></table></figure></p><p>其实也很简单,我们只需要定义上面的接口就可以了,我们用 Field 声明了表单的项,这样提交表单就跟普通的函数调用一样简单直接了。<br>等等,你说你的表单项不确定个数?还是说有很多项你懒得写?Field 同样有个打群架的版本——FieldMap,赶紧试试吧~~</p><h4 id="Part-amp-PartMap"><a href="#Part-amp-PartMap" class="headerlink" title="Part &amp; PartMap"></a>Part &amp; PartMap</h4><p>这个是用来上传文件的。话说当年用 HttpClient 上传个文件老费劲了,一会儿编码不对,一会儿参数错误(也怪那时段位太低吧TT)。。。可是现在不同了,自从有了 Retrofit,妈妈再也不用担心文件上传费劲了~~~<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">FileUploadService</span> </span>&#123; </span><br><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></span><br><span class="line"><span class="function"> @Part MultipartBody.Part file)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果你需要上传文件,和我们前面的做法类似,定义一个接口方法,需要注意的是,这个方法不再有 @FormUrlEncoded 这个注解,而换成了 @Multipart,后面只需要在参数中增加 Part 就可以了。也许你会问,这里的 Part 和 Field 究竟有什么区别,其实从功能上讲,无非就是客户端向服务端发起请求携带参数的方式不同,并且前者可以携带的参数类型更加丰富,包括数据流。也正是因为这一点,我们可以通过这种方式来上传文件,下面我们就给出这个接口的使用方法:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先创建 service</span></span><br><span class="line">FileUploadService service = retrofit.create(FileUploadService.class);</span><br><span class="line"> </span><br><span class="line"><span class="comment">//构建要上传的文件</span></span><br><span class="line">File file = <span class="keyword">new</span> File(filename);</span><br><span class="line">RequestBody requestFile =</span><br><span class="line"> RequestBody.create(MediaType.parse(<span class="string">"application/otcet-stream"</span>), file);</span><br><span class="line"> </span><br><span class="line">MultipartBody.Part body =</span><br><span class="line"> MultipartBody.Part.createFormData(<span class="string">"aFile"</span>, file.getName(), requestFile);</span><br><span class="line"> </span><br><span class="line">String descriptionString = <span class="string">"This is a description"</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><span class="line"> </span><br><span class="line">Call&lt;ResponseBody&gt; call = service.upload(description, body);</span><br><span class="line">call.enqueue(<span class="keyword">new</span> Callback&lt;ResponseBody&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;ResponseBody&gt; call,</span></span></span><br><span class="line"><span class="function"><span class="params"> Response&lt;ResponseBody&gt; response)</span> </span>&#123;</span><br><span class="line"> System.out.println(<span class="string">"success"</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;ResponseBody&gt; call, Throwable t)</span> </span>&#123;</span><br><span class="line"> t.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>在实验时,我上传了一个只包含一行文字的文件:<br>Visit me: <a href="http://www.println.net" target="_blank" rel="noopener">http://www.println.net</a><br>那么我们去服务端看下我们的请求是什么样的:<br>HEADERS<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">Accept-Encoding: gzip</span><br><span class="line">Content-Length: 470</span><br><span class="line">Content-Type: multipart/form-data; boundary=9b670d44-63dc-4a8a-833d-66e45e0156ca</span><br><span class="line">User-Agent: okhttp/3.2.0</span><br><span class="line">X-Request-Id: 9d70e8cc-958b-4f42-b979-4c1fcd474352</span><br><span class="line">Via: 1.1 vegur</span><br><span class="line">Host: requestb.in</span><br><span class="line">Total-Route-Time: 0</span><br><span class="line">Connection: close</span><br><span class="line">Connect-Time: 0</span><br></pre></td></tr></table></figure></p><p>FORM/POST PARAMETERS<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">description: This is a description</span><br></pre></td></tr></table></figure></p><p>RAW BODY<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">--9b670d44-63dc-4a8a-833d-66e45e0156ca</span><br><span class="line">Content-Disposition: form-data; name="description"</span><br><span class="line">Content-Transfer-Encoding: binary</span><br><span class="line">Content-Type: multipart/form-data; charset=utf-8</span><br><span class="line">Content-Length: 21</span><br><span class="line"></span><br><span class="line">This is a description</span><br><span class="line">--9b670d44-63dc-4a8a-833d-66e45e0156ca</span><br><span class="line">Content-Disposition: form-data; name="aFile"; filename="uploadedfile.txt"</span><br><span class="line">Content-Type: application/otcet-stream</span><br><span class="line">Content-Length: 32</span><br><span class="line"></span><br><span class="line">Visit me: http://www.println.net</span><br><span class="line">--9b670d44-63dc-4a8a-833d-66e45e0156ca--</span><br></pre></td></tr></table></figure></p><p>我们看到,我们上传的文件的内容出现在请求当中了。如果你需要上传多个文件,就声明多个 Part 参数,或者试试 PartMap。</p><h3 id="Converter,让你的入参和返回类型丰富起来"><a href="#Converter,让你的入参和返回类型丰富起来" class="headerlink" title="Converter,让你的入参和返回类型丰富起来"></a>Converter,让你的入参和返回类型丰富起来</h3><h4 id="RequestBodyConverter"><a href="#RequestBodyConverter" class="headerlink" title="RequestBodyConverter"></a>RequestBodyConverter</h4><p>前面我为大家展示了如何用 Retrofit 上传文件,这个上传的过程其实。。还是有那么点儿不够简练,我们只是要提供一个文件用于上传,可我们前后构造了三个对象:<br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/Retrofit/3.jpg"><br>天哪,肯定是哪里出了问题。实际上,Retrofit 允许我们自己定义入参和返回的类型,不过,如果这些类型比较特别,我们还需要准备相应的 Converter,也正是因为 Converter 的存在, Retrofit 在入参和返回类型上表现得非常灵活。<br>下面我们把刚才的 Service 代码稍作修改:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">FileUploadService</span> </span>&#123; </span><br><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></span><br><span class="line"><span class="function"> <span class="comment">//注意这里的参数 "aFile" 之前是在创建 MultipartBody.Part 的时候传入的</span></span></span><br><span class="line"><span class="function"> @<span class="title">Part</span><span class="params">(<span class="string">"aFile"</span>)</span> File file)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在我们把入参类型改成了我们熟悉的 File,如果你就这么拿去发请求,服务端收到的结果会让你哭了的。。。</p><p>RAW BODY<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">--7d24e78e-4354-4ed4-9db4-57d799b6efb7</span><br><span class="line">Content-Disposition: form-data; name="description"</span><br><span class="line">Content-Transfer-Encoding: binary</span><br><span class="line">Content-Type: multipart/form-data; charset=utf-8</span><br><span class="line">Content-Length: 21</span><br><span class="line"></span><br><span class="line">This is a description</span><br><span class="line">--7d24e78e-4354-4ed4-9db4-57d799b6efb7</span><br><span class="line">Content-Disposition: form-data; name="aFile"</span><br><span class="line">Content-Transfer-Encoding: binary</span><br><span class="line">Content-Type: application/json; charset=UTF-8</span><br><span class="line">Content-Length: 35</span><br><span class="line"></span><br><span class="line">// 注意这里!!之前是文件的内容,现在变成了文件的路径</span><br><span class="line">&#123;"path":"samples/uploadedfile.txt"&#125; </span><br><span class="line">--7d24e78e-4354-4ed4-9db4-57d799b6efb7--</span><br></pre></td></tr></table></figure></p><p>服务端收到了一个文件的路径,它肯定会觉得<br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/Retrofit/4.jpg"><br>好了,不闹了,这明显是 Retrofit 在发现自己收到的实际入参是个 File 时,不知道该怎么办,情急之下给 toString了,而且还是个 JsonString(后来查证原来是使用了 GsonRequestBodyConverter。。)。</p><p>接下来我们就自己实现一个 FileRequestBodyConverter,<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">FileRequestBodyConverterFactory</span> <span class="keyword">extends</span> <span class="title">Converter</span>.<span class="title">Factory</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> Converter&lt;File, RequestBody&gt; <span class="title">requestBodyConverter</span><span class="params">(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> FileRequestBodyConverter();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">FileRequestBodyConverter</span> <span class="keyword">implements</span> <span class="title">Converter</span>&lt;<span class="title">File</span>, <span class="title">RequestBody</span>&gt; </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> RequestBody <span class="title">convert</span><span class="params">(File file)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> RequestBody.create(MediaType.parse(<span class="string">"application/otcet-stream"</span>), file);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在创建 Retrofit 的时候记得配置上它:<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">addConverterFactory(<span class="keyword">new</span> FileRequestBodyConverterFactory())</span><br></pre></td></tr></table></figure></p><p>这样,我们的文件内容就能上传了。来,看下结果吧:</p><p>RAW BODY<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">--25258f46-48b0-4a6b-a617-15318c168ed4</span><br><span class="line">Content-Disposition: form-data; name="description"</span><br><span class="line">Content-Transfer-Encoding: binary</span><br><span class="line">Content-Type: multipart/form-data; charset=utf-8</span><br><span class="line">Content-Length: 21</span><br><span class="line"></span><br><span class="line">This is a description</span><br><span class="line">--25258f46-48b0-4a6b-a617-15318c168ed4</span><br><span class="line">//注意看这里,filename 没了</span><br><span class="line">Content-Disposition: form-data; name="aFile"</span><br><span class="line">//多了这一句</span><br><span class="line">Content-Transfer-Encoding: binary</span><br><span class="line">Content-Type: application/otcet-stream</span><br><span class="line">Content-Length: 32</span><br><span class="line"></span><br><span class="line">Visit me: http://www.println.net</span><br><span class="line">--25258f46-48b0-4a6b-a617-15318c168ed4--</span><br></pre></td></tr></table></figure></p><p>文件内容成功上传了,当然其中还存在一些问题,这个目前直接使用 Retrofit 的 Converter 还做不到,原因主要在于我们没有办法通过 Converter 直接将 File 转换为 MultiPartBody.Part,如果想要做到这一点,我们可以对 Retrofit 的源码稍作修改,这个我们后面再谈。</p><h4 id="ResponseBodyConverter"><a href="#ResponseBodyConverter" class="headerlink" title="ResponseBodyConverter"></a>ResponseBodyConverter</h4><p>前面我们为大家简单示例了如何自定义 RequestBodyConverter,对应的,Retrofit 也支持自定义 ResponseBodyConverter。</p><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="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></pre></td></tr></table></figure></p><p>返回值的类型为 List<repo>,而我们直接拿到的原始返回肯定就是字符串(或者字节流),那么这个返回值类型是怎么来的呢?首先说明的一点是,GitHub 的这个 api 返回的是 Json 字符串,也就是说,我们需要使用 Json 反序列化得到 List<repo>,这其中用到的其实是 GsonResponseBodyConverter。</repo></repo></p><p>问题来了,如果请求得到的 Json 字符串与返回值类型不对应,比如:</p><p>接口返回的 Json 字符串:<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="attr">"err"</span>:<span class="number">0</span>, <span class="attr">"content"</span>:<span class="string">"This is a content."</span>, <span class="attr">"message"</span>:<span class="string">"OK"</span>&#125;</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><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="class"><span class="keyword">class</span> <span class="title">Result</span></span>&#123;</span><br><span class="line"> <span class="keyword">int</span> code;<span class="comment">//等价于 err</span></span><br><span class="line"> String body;<span class="comment">//等价于 content</span></span><br><span class="line"> String msg;<span class="comment">//等价于 message</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>哇,这时候肯定有人想说,你是不是脑残,偏偏跟服务端对着干?哈哈,我只是示例嘛,而且在生产环境中,你敢保证这种情况不会发生??<br>这种情况下, Gson 就是再牛逼,也只能默默无语俩眼泪了,它哪儿知道字段的映射关系怎么这么任性啊。好,现在让我们自定义一个 Converter 来解决这个问题吧!<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ArbitraryResponseBodyConverterFactory</span> <span class="keyword">extends</span> <span class="title">Converter</span>.<span class="title">Factory</span></span>&#123;</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, Retrofit retrofit) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.responseBodyConverter(type, annotations, retrofit);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ArbitraryResponseBodyConverter</span> <span class="keyword">implements</span> <span class="title">Converter</span>&lt;<span class="title">ResponseBody</span>, <span class="title">Result</span>&gt;</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> Result <span class="title">convert</span><span class="params">(ResponseBody value)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> RawResult rawResult = <span class="keyword">new</span> Gson().fromJson(value.string(), RawResult.class);</span><br><span class="line"> Result result = <span class="keyword">new</span> Result();</span><br><span class="line"> result.body = rawResult.content;</span><br><span class="line"> result.code = rawResult.err;</span><br><span class="line"> result.msg = rawResult.message;</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">RawResult</span></span>&#123;</span><br><span class="line"> <span class="keyword">int</span> err;</span><br><span class="line"> String content;</span><br><span class="line"> String message;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>当然,别忘了在构造 Retrofit 的时候添加这个 Converter,这样我们就能够愉快的让接口返回 Result 对象了。</p><p>注意!!Retrofit 在选择合适的 Converter 时,主要依赖于需要转换的对象类型,在添加 Converter 时,注意 Converter 支持的类型的包含关系以及其顺序。</p><h2 id="Retrofit-原理剖析"><a href="#Retrofit-原理剖析" class="headerlink" title="Retrofit 原理剖析"></a>Retrofit 原理剖析</h2><p>前一个小节我们把 Retrofit 的基本用法和概念介绍了一下,如果你的目标是学会如何使用它,那么下面的内容你可以不用看了。</p><p>不过呢,我就知道你不是那种浅尝辄止的人!这一节我们主要把注意力放在 Retrofit 背后的魔法上面~~<br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/Retrofit/5.jpg"></p><h3 id="是谁实际上完成了接口请求的处理?"><a href="#是谁实际上完成了接口请求的处理?" class="headerlink" title="是谁实际上完成了接口请求的处理?"></a>是谁实际上完成了接口请求的处理?</h3><p>前面讲了这么久,我们始终只看到了我们自己定义的接口,比如:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></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">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></pre></td></tr></table></figure></p><p>而真正我使用的时候肯定不能是接口啊,这个神秘的家伙究竟是谁?其实它是 Retrofit 创建的一个代理对象了,这里涉及点儿 Java 的动态代理的知识,直接来看代码:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; <span class="function">T <span class="title">create</span><span class="params">(<span class="keyword">final</span> Class&lt;T&gt; service)</span> </span>&#123;</span><br><span class="line"> Utils.validateServiceInterface(service);</span><br><span class="line"> <span class="keyword">if</span> (validateEagerly) &#123;</span><br><span class="line"> eagerlyValidateMethods(service);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//这里返回一个 service 的代理对象</span></span><br><span class="line"> <span class="keyword">return</span> (T) Proxy.newProxyInstance(service.getClassLoader(), <span class="keyword">new</span> Class&lt;?&gt;[] &#123; service &#125;,</span><br><span class="line"> <span class="keyword">new</span> InvocationHandler() &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Platform platform = Platform.get();</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object... args)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line"> <span class="comment">// If the method is a method from Object then defer to normal invocation.</span></span><br><span class="line"> <span class="keyword">if</span> (method.getDeclaringClass() == Object.class) &#123;</span><br><span class="line"> <span class="keyword">return</span> method.invoke(<span class="keyword">this</span>, args);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//DefaultMethod 是 Java 8 的概念,是定义在 interface 当中的有实现的方法</span></span><br><span class="line"> <span class="keyword">if</span> (platform.isDefaultMethod(method)) &#123;</span><br><span class="line"> <span class="keyword">return</span> platform.invokeDefaultMethod(method, service, proxy, args);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//每一个接口最终实例化成一个 ServiceMethod,并且会缓存</span></span><br><span class="line"> ServiceMethod serviceMethod = loadServiceMethod(method);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//由此可见 Retrofit 与 OkHttp 完全耦合,不可分割</span></span><br><span class="line"> OkHttpCall okHttpCall = <span class="keyword">new</span> OkHttpCall&lt;&gt;(serviceMethod, args);</span><br><span class="line"> <span class="comment">//下面这一句当中会发起请求,并解析服务端返回的结果</span></span><br><span class="line"> <span class="keyword">return</span> serviceMethod.callAdapter.adapt(okHttpCall);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>简单的说,在我们调用 GitHubService.listRepos 时,实际上调用的是这里的 InvocationHandler.invoke 方法~~</p><h3 id="来一发完整的请求处理流程"><a href="#来一发完整的请求处理流程" class="headerlink" title="来一发完整的请求处理流程"></a>来一发完整的请求处理流程</h3><p>前面我们已经看到 Retrofit 为我们构造了一个 OkHttpCall ,实际上每一个 OkHttpCall 都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是 OkHttpCall 了,如果我们添加了自定义的 callAdapter,那么它就会将 OkHttp 适配成我们需要的返回值,并返回给我们。</p><p>先来看下 Call 的接口:<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">interface</span> <span class="title">Call</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">Cloneable</span> </span>&#123;</span><br><span class="line"> <span class="comment">//同步发起请求</span></span><br><span class="line"> <span class="function">Response&lt;T&gt; <span class="title">execute</span><span class="params">()</span> <span class="keyword">throws</span> IOException</span>;</span><br><span class="line"> <span class="comment">//异步发起请求,结果通过回调返回</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">enqueue</span><span class="params">(Callback&lt;T&gt; callback)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isExecuted</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">cancel</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isCanceled</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function">Call&lt;T&gt; <span class="title">clone</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="comment">//返回原始请求</span></span><br><span class="line"> <span class="function">Request <span class="title">request</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</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><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Call&lt;List&lt;Repo&gt;&gt; repos = service.listRepos(<span class="string">"octocat"</span>);</span><br><span class="line">List&lt;Repo&gt; data = repos.execute();</span><br></pre></td></tr></table></figure></p><p>这个 repos 其实就是一个 OkHttpCall 实例,execute 就是要发起网络请求。<br>OkHttpCall.execute<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> Response&lt;T&gt; <span class="title">execute</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> <span class="comment">//这个 call 是真正的 OkHttp 的 call,本质上 OkHttpCall 只是对它做了一层封装</span></span><br><span class="line"> okhttp3.Call call;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line"> <span class="comment">//处理重复执行的逻辑</span></span><br><span class="line"> <span class="keyword">if</span> (executed) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Already executed."</span>);</span><br><span class="line"> executed = <span class="keyword">true</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (creationFailure != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">if</span> (creationFailure <span class="keyword">instanceof</span> IOException) &#123;</span><br><span class="line"> <span class="keyword">throw</span> (IOException) creationFailure;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">throw</span> (RuntimeException) creationFailure;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> call = rawCall;</span><br><span class="line"> <span class="keyword">if</span> (call == <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> call = rawCall = createRawCall();</span><br><span class="line"> &#125; <span class="keyword">catch</span> (IOException | RuntimeException e) &#123;</span><br><span class="line"> creationFailure = e;</span><br><span class="line"> <span class="keyword">throw</span> e;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (canceled) &#123;</span><br><span class="line"> call.cancel();</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//发起请求,并解析结果</span></span><br><span class="line"> <span class="keyword">return</span> parseResponse(call.execute());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们看到 OkHttpCall 其实也是封装了 okhttp3.Call,在这个方法中,我们通过 okhttp3.Call 发起了进攻,额,发起了请求。有关 OkHttp 的内容,我在这里就不再展开了。<br>parseResponse 主要完成了由 okhttp3.Response 向 retrofit.Response 的转换,同时也处理了对原始返回的解析:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Response&lt;T&gt; <span class="title">parseResponse</span><span class="params">(okhttp3.Response rawResponse)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> ResponseBody rawBody = rawResponse.body();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//略掉一些代码</span></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">//在这里完成了原始 Response 的解析,T 就是我们想要的结果,比如 GitHubService.listRepos 的 List&lt;Repo&gt;</span></span><br><span class="line"> T body = serviceMethod.toResponse(catchingBody);</span><br><span class="line"> <span class="keyword">return</span> Response.success(body, rawResponse);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (RuntimeException e) &#123;</span><br><span class="line"> <span class="comment">// If the underlying source threw an exception, propagate that rather than indicating it was</span></span><br><span class="line"> <span class="comment">// a runtime exception.</span></span><br><span class="line"> catchingBody.throwIfCaught();</span><br><span class="line"> <span class="keyword">throw</span> e;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>至此,我们就拿到了我们想要的数据~~</p><h3 id="结果适配,你是不是想用-RxJava?"><a href="#结果适配,你是不是想用-RxJava?" class="headerlink" title="结果适配,你是不是想用 RxJava?"></a>结果适配,你是不是想用 RxJava?</h3><p>前面我们已经提到过 CallAdapter 的事儿,默认情况下,它并不会对 OkHttpCall 实例做任何处理:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultCallAdapterFactory</span> <span class="keyword">extends</span> <span class="title">CallAdapter</span>.<span class="title">Factory</span> </span>&#123;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">final</span> CallAdapter.Factory INSTANCE = <span class="keyword">new</span> DefaultCallAdapterFactory();</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> CallAdapter&lt;?&gt; get(Type returnType, Annotation[] annotations, Retrofit retrofit) &#123;</span><br><span class="line"> ... 毫不留情的省略一些代码 ...</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> CallAdapter&lt;Call&lt;?&gt;&gt;() &#123;</span><br><span class="line"> ... 省略一些代码 ...</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> <span class="keyword">public</span> &lt;R&gt; <span class="function">Call&lt;R&gt; <span class="title">adapt</span><span class="params">(Call&lt;R&gt; call)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//看这里,直接把传入的 call 返回了</span></span><br><span class="line"> <span class="keyword">return</span> call;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>现在的需求是,我想要接入 RxJava,让接口的返回结果改为 Observable:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GitHub</span> </span>&#123;</span><br><span class="line"> <span class="meta">@GET</span>(<span class="string">"/repos/&#123;owner&#125;/&#123;repo&#125;/contributors"</span>)</span><br><span class="line"> Observable&lt;List&lt;Contributor&gt;&gt; contributors(</span><br><span class="line"> <span class="meta">@Path</span>(<span class="string">"owner"</span>) String owner,</span><br><span class="line"> <span class="meta">@Path</span>(<span class="string">"repo"</span>) String repo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可不可以呢?当然是可以的,只需要提供一个 Adapter,将 OkHttpCall 转换为 Observable 即可呀!Retrofit 的开发者们早就想到了这个问题,并且为我们提供了相应的 Adapter:<br>RxJavaCallAdapterFactory<br>我们只需要在构造 Retrofit 时,添加它:<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">addCallAdapterFactory(RxJavaCallAdapterFactory.create())</span><br></pre></td></tr></table></figure></p><p>这样我们的接口就可以以 RxJava 的方式工作了。</p><p>好,歇会儿,抽一袋烟。。。</p><p>接着我们搞清楚 RxJavaCallAdapterFactory 是怎么工作的,首先让我们来看下 CallAdapter 的接口:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></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">CallAdapter</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> *返回 Http 返回解析后的类型。需要注意的是这个并不是接口的返回类型,</span></span><br><span class="line"><span class="comment"> *而是接口返回类型中的泛型参数的实参。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function">Type <span class="title">responseType</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * T 是我们需要转换成的接口返回类型,参数 call 其实最初就是 OkHttpCall 的实例</span></span><br><span class="line"><span class="comment"> * 在这里 T 其实是 RxJava 支持的类型,比如 Observable</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> &lt;R&gt; <span class="function">T <span class="title">adapt</span><span class="params">(Call&lt;R&gt; call)</span></span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//我们需要将 Factory 的子类对应的实例在构造 Retrofit 时添加到其中。</span></span><br><span class="line"> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line"> <span class="comment">//根据接口的返回类型(Observable&lt;T&gt;),注解类型等等来判断是否是当前 Adapter 支持的类型,不是则返回null</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> CallAdapter&lt;?&gt; get(Type returnType, Annotation[] annotations,</span><br><span class="line"> Retrofit retrofit);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//获取指定 index 的泛型参数的上限,比如对于 Map&lt;String, ? extends Number&gt;,index为 1 的参数上限是 Number</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">static</span> Type <span class="title">getParameterUpperBound</span><span class="params">(<span class="keyword">int</span> index, ParameterizedType type)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> Utils.getParameterUpperBound(index, type);</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"> * 获取原始类型,比如 List&lt;String&gt; 返回 List.class,这里传入的 type 情况可能比较复杂,因此不能直接当做</span></span><br><span class="line"><span class="comment"> * Class 去做判断。这个方法在判断类型是否为支持的类型时经常用到。</span></span><br><span class="line"><span class="comment"> protected static Class&lt;?&gt; getRawType(Type type) &#123;</span></span><br><span class="line"><span class="comment"> return Utils.getRawType(type);</span></span><br><span class="line"><span class="comment"> &#125;</span></span><br><span class="line"><span class="comment"> &#125;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br></pre></td></tr></table></figure></p><p>代码中做了较为详细的注释,简单来说,我们只需要实现 CallAdapter 类来提供具体的适配逻辑,并实现相应的 Factory,用来将当前的 CallAdapter注册到 Retrofit 当中,并在 Factory.get 方法中根据类型来返回当前的 CallAdapter即可。知道了这些,我们再来看 RxJavaCallAdapterFactory:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">RxJavaCallAdapterFactory</span> <span class="keyword">extends</span> <span class="title">CallAdapter</span>.<span class="title">Factory</span> </span>&#123;</span><br><span class="line"> </span><br><span class="line"> ... 请叫我省略君,为了省地方,一个都不放过! ...</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> CallAdapter&lt;?&gt; get(Type returnType, Annotation[] annotations, Retrofit retrofit) &#123;</span><br><span class="line"> <span class="comment">//注意下面的代码主要是判断 returnType 是否为 RxJava 支持的类型</span></span><br><span class="line"> Class&lt;?&gt; rawType = getRawType(returnType);</span><br><span class="line"> String canonicalName = rawType.getCanonicalName();</span><br><span class="line"> <span class="keyword">boolean</span> isSingle = <span class="string">"rx.Single"</span>.equals(canonicalName);</span><br><span class="line"> <span class="keyword">boolean</span> isCompletable = <span class="string">"rx.Completable"</span>.equals(canonicalName);</span><br><span class="line"> <span class="keyword">if</span> (rawType != Observable.class &amp;&amp; !isSingle &amp;&amp; !isCompletable) &#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"> ... 这里省略掉的代码主要是根据返回类型获取合适的 Adapter ...</span><br><span class="line"> <span class="keyword">return</span> callAdapter;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> ... 我又来了,继续略去一些代码 ...</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleCallAdapter</span> <span class="keyword">implements</span> <span class="title">CallAdapter</span>&lt;<span class="title">Observable</span>&lt;?&gt;&gt; </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Type responseType;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Scheduler scheduler;</span><br><span class="line"> </span><br><span class="line"> SimpleCallAdapter(Type responseType, Scheduler scheduler) &#123;</span><br><span class="line"> <span class="keyword">this</span>.responseType = responseType;</span><br><span class="line"> <span class="keyword">this</span>.scheduler = scheduler;</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> Type <span class="title">responseType</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> responseType;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> <span class="keyword">public</span> &lt;R&gt; <span class="function">Observable&lt;R&gt; <span class="title">adapt</span><span class="params">(Call&lt;R&gt; call)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//在这里创建需作为返回值的 Observable 实例,并持有 call 实例</span></span><br><span class="line"> <span class="comment">//可以想象得到,在 Observable.subscribe 触发时, call.execute 将会被调用</span></span><br><span class="line"> Observable&lt;R&gt; observable = Observable.create(<span class="keyword">new</span> CallOnSubscribe&lt;&gt;(call)) </span><br><span class="line"> .lift(OperatorMapResponseToBodyOrError.&lt;R&gt;instance());</span><br><span class="line"> <span class="keyword">if</span> (scheduler != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> observable.subscribeOn(scheduler);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> observable;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//... 略去一些代码 ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>RxJavaCallAdapterFactory 提供了不止一种 Adapter,但原理大同小异,有兴趣的读者可以自行参阅其源码。</p><p>至此,我们已经对 CallAdapter 的机制有了一个清晰的认识了。</p><h2 id="几个进阶玩法"><a href="#几个进阶玩法" class="headerlink" title="几个进阶玩法"></a>几个进阶玩法</h2><p>前面我们已经介绍了很多东西了。。可,挖掘机专业的同学们,你们觉得这就够了么?当然是不够!</p><h3 id="继续简化文件上传的接口"><a href="#继续简化文件上传的接口" class="headerlink" title="继续简化文件上传的接口"></a>继续简化文件上传的接口</h3><p>在前面我们曾试图简化文件上传接口的使用,尽管我们已经给出了相应的 File -&gt; RequestBody 的 Converter,不过基于 Retrofit本身的限制,我们还是不能像直接构造 MultiPartBody.Part 那样来获得更多的灵活性。这时候该怎么办?当然是 Hack~~</p><p>首先明确我们的需求:</p><ul><li>文件的 Content-Type 需要更多的灵活性,不应该写死在 Converter 当中,可以的话,最好可以根据文件的扩展名来映射出来对应的 Content-Type, 比如 image.png -&gt; image/png;</li><li>在请求的数据中,能够正常携带 filename 这个字段。</li></ul><p>为此,我增加了一套完整的参数解析方案:</p><ul><li>增加任意类型转换的 Converter,这一步主要是满足后续我们直接将入参类型转换为 MultiPartBody.Part 类型:</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="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Converter</span>&lt;<span class="title">F</span>, <span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line"> ...</span><br><span class="line"> <span class="comment">//返回一个满足条件的不限制类型的 Converter</span></span><br><span class="line"> <span class="keyword">public</span> Converter&lt;?, ?&gt; arbitraryConverter(Type originalType,</span><br><span class="line"> Type convertedType, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, 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><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需要注意的是,Retrofit 类当中也需要增加相应的方法:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;F, T&gt; <span class="function">Converter&lt;F, T&gt; <span class="title">arbitraryConverter</span><span class="params">(Type orignalType,</span></span></span><br><span class="line"><span class="function"><span class="params"> Type convertedType, Annotation[] parameterAnnotations, Annotation[] methodAnnotations)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> nextArbitraryConverter(<span class="keyword">null</span>, orignalType, convertedType, parameterAnnotations, methodAnnotations);</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> &lt;F, T&gt; <span class="function">Converter&lt;F, T&gt; <span class="title">nextArbitraryConverter</span><span class="params">(Converter.Factory skipPast,</span></span></span><br><span class="line"><span class="function"><span class="params"> Type type, Type convertedType, Annotation[] parameterAnnotations, Annotation[] methodAnnotations)</span> </span>&#123;</span><br><span class="line"> checkNotNull(type, <span class="string">"type == null"</span>);</span><br><span class="line"> checkNotNull(parameterAnnotations, <span class="string">"parameterAnnotations == null"</span>);</span><br><span class="line"> checkNotNull(methodAnnotations, <span class="string">"methodAnnotations == null"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> start = converterFactories.indexOf(skipPast) + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = start, count = converterFactories.size(); i &lt; count; i++) &#123;</span><br><span class="line"> Converter.Factory factory = converterFactories.get(i);</span><br><span class="line"> Converter&lt;?, ?&gt; converter =</span><br><span class="line"> factory.arbitraryConverter(type, convertedType, parameterAnnotations, methodAnnotations, <span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">if</span> (converter != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="comment">//noinspection unchecked</span></span><br><span class="line"> <span class="keyword">return</span> (Converter&lt;F, T&gt;) converter;</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></pre></td></tr></table></figure></p><ul><li>再给出 arbitraryConverter 的具体实现:</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TypedFileMultiPartBodyConverterFactory</span> <span class="keyword">extends</span> <span class="title">Converter</span>.<span class="title">Factory</span> </span>&#123;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Converter&lt;TypedFile, MultipartBody.Part&gt; arbitraryConverter(Type originalType, Type convertedType, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) &#123;</span><br><span class="line"> <span class="keyword">if</span> (originalType == TypedFile.class &amp;&amp; convertedType == MultipartBody.Part.class) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> FileRequestBodyConverter();</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></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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TypedFileMultiPartBodyConverter</span> <span class="keyword">implements</span> <span class="title">Converter</span>&lt;<span class="title">TypedFile</span>, <span class="title">MultipartBody</span>.<span class="title">Part</span>&gt; </span>&#123;</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> MultipartBody.<span class="function">Part <span class="title">convert</span><span class="params">(TypedFile typedFile)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> RequestBody requestFile =</span><br><span class="line"> RequestBody.create(typedFile.getMediaType(), typedFile.getFile());</span><br><span class="line"> <span class="keyword">return</span> MultipartBody.Part.createFormData(typedFile.getName(), typedFile.getFile().getName(), requestFile);</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><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">TypedFile</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> MediaType mediaType;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> File file;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">TypedFile</span><span class="params">(String name, String filepath)</span></span>&#123;</span><br><span class="line"> <span class="keyword">this</span>(name, <span class="keyword">new</span> File(filepath));</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">TypedFile</span><span class="params">(String name, File file)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>(MediaType.parse(MediaTypes.getMediaType(file)), name, file);</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">TypedFile</span><span class="params">(MediaType mediaType, String name, String filepath)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>(mediaType, name, <span class="keyword">new</span> File(filepath));</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">TypedFile</span><span class="params">(MediaType mediaType, String name, File file)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.mediaType = mediaType;</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.file = file;</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"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> MediaType <span class="title">getMediaType</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> mediaType;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> File <span class="title">getFile</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> file;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在声明接口时,@Part 不要传入参数,这样 Retrofit 在 ServiceMethod.Builder.parseParameterAnnotation 方法中解析 Part时,就会认为我们传入的参数为 MultiPartBody.Part 类型(实际上我们将在后面自己转换)。那么解析的时候,我们拿到前面定义好的 Converter,构造一个 ParameterHandler:</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></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) &#123;</span><br><span class="line"> <span class="keyword">return</span> ParameterHandler.RawPart.INSTANCE;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> Converter&lt;?, ?&gt; converter =</span><br><span class="line"> retrofit.arbitraryConverter(type, MultipartBody.Part.class, annotations, methodAnnotations);</span><br><span class="line"> <span class="keyword">if</span>(converter == <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> parameterError(p,</span><br><span class="line"> <span class="string">"@Part annotation must supply a name or use MultipartBody.Part parameter type."</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ParameterHandler.TypedFileHandler((Converter&lt;TypedFile, MultipartBody.Part&gt;) converter);</span><br><span class="line">&#125;</span><br><span class="line">...</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">TypedFileHandler</span> <span class="keyword">extends</span> <span class="title">ParameterHandler</span>&lt;<span class="title">TypedFile</span>&gt;</span>&#123;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Converter&lt;TypedFile, MultipartBody.Part&gt; converter;</span><br><span class="line"> </span><br><span class="line"> TypedFileHandler(Converter&lt;TypedFile, MultipartBody.Part&gt; converter) &#123;</span><br><span class="line"> <span class="keyword">this</span>.converter = converter;</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">void</span> <span class="title">apply</span><span class="params">(RequestBuilder builder, TypedFile value)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"> <span class="keyword">if</span>(value != <span class="keyword">null</span>)&#123;</span><br><span class="line"> builder.addPart(converter.convert(value));</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><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><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">FileUploadService</span> </span>&#123;</span><br><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></span><br><span class="line"><span class="function"> @Part TypedFile typedFile)</span>;</span><br><span class="line">&#125;</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><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">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line"> .baseUrl(<span class="string">"http://www.println.net/"</span>)</span><br><span class="line"> .addConverterFactory(<span class="keyword">new</span> TypedFileMultiPartBodyConverterFactory())</span><br><span class="line"> .addConverterFactory(GsonConverterFactory.create())</span><br><span class="line"> .build();</span><br><span class="line"> </span><br><span class="line">FileUploadService service = retrofit.create(FileUploadService.class);</span><br><span class="line">TypedFile typedFile = <span class="keyword">new</span> TypedFile(<span class="string">"aFile"</span>, filename);</span><br><span class="line">String descriptionString = <span class="string">"This is a description"</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><span class="line"> </span><br><span class="line">Call&lt;ResponseBody&gt; call = service.upload(description, typedFile);</span><br><span class="line">call.enqueue(...);</span><br></pre></td></tr></table></figure></p><p>至此,我们已经通过自己的双手,让 Retrofit 的点亮了自定义上传文件的技能,风骚等级更上一层楼!</p><h3 id="Mock-Server"><a href="#Mock-Server" class="headerlink" title="Mock Server"></a>Mock Server</h3><p>我们在开发过程中,经常遇到服务端不稳定的情况,测试开发环境,这是难免的。于是我们需要能够模拟网络请求来调试我们的客户端逻辑,Retrofit 自然是支持这个功能的。</p><p>真是太贴心,Retrofit 提供了一个 MockServer 的功能,可以在几乎不改动客户端原有代码的前提下,实现接口数据返回的自定义,我们在自己的工程中增加下面的依赖:<br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">compile <span class="string">'com.squareup.retrofit2:retrofit-mock:2.0.2</span></span><br></pre></td></tr></table></figure></p><p>还是先让我们来看看官方 demo,首先定义了一个 GituHb api,好熟悉的感觉:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">GitHub</span> </span>&#123;</span><br><span class="line"> <span class="meta">@GET</span>(<span class="string">"/repos/&#123;owner&#125;/&#123;repo&#125;/contributors"</span>)</span><br><span class="line"> Call&lt;List&lt;Contributor&gt;&gt; contributors(</span><br><span class="line"> <span class="meta">@Path</span>(<span class="string">"owner"</span>) String owner,</span><br><span class="line"> <span class="meta">@Path</span>(<span class="string">"repo"</span>) String repo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这就是我们要请求的接口了,怎么 Mock 呢?</p><ul><li>定义一个接口实现类 MockGitHub,我们可以看到,所有我们需要请求的接口都在这里得到了实现,也就是说,我们待会儿调用 GitHub 的 api 时,实际上是访问 MockGitHub 的方法:</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">MockGitHub</span> <span class="keyword">implements</span> <span class="title">GitHub</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> BehaviorDelegate&lt;GitHub&gt; delegate;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Map&lt;String, List&lt;Contributor&gt;&gt;&gt; ownerRepoContributors;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MockGitHub</span><span class="params">(BehaviorDelegate&lt;GitHub&gt; delegate)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.delegate = delegate;</span><br><span class="line"> ownerRepoContributors = <span class="keyword">new</span> LinkedHashMap&lt;&gt;();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Seed some mock data.</span></span><br><span class="line"> addContributor(<span class="string">"square"</span>, <span class="string">"retrofit"</span>, <span class="string">"John Doe"</span>, <span class="number">12</span>);</span><br><span class="line"> addContributor(<span class="string">"square"</span>, <span class="string">"retrofit"</span>, <span class="string">"Bob Smith"</span>, <span class="number">2</span>);</span><br><span class="line"> addContributor(<span class="string">"square"</span>, <span class="string">"retrofit"</span>, <span class="string">"Big Bird"</span>, <span class="number">40</span>);</span><br><span class="line"> addContributor(<span class="string">"square"</span>, <span class="string">"picasso"</span>, <span class="string">"Proposition Joe"</span>, <span class="number">39</span>);</span><br><span class="line"> addContributor(<span class="string">"square"</span>, <span class="string">"picasso"</span>, <span class="string">"Keiser Soze"</span>, <span class="number">152</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@Override</span> <span class="keyword">public</span> Call&lt;List&lt;Contributor&gt;&gt; contributors(String owner, String repo) &#123;</span><br><span class="line"> List&lt;Contributor&gt; response = Collections.emptyList();</span><br><span class="line"> Map&lt;String, List&lt;Contributor&gt;&gt; repoContributors = ownerRepoContributors.get(owner);</span><br><span class="line"> <span class="keyword">if</span> (repoContributors != <span class="keyword">null</span>) &#123;</span><br><span class="line"> List&lt;Contributor&gt; contributors = repoContributors.get(repo);</span><br><span class="line"> <span class="keyword">if</span> (contributors != <span class="keyword">null</span>) &#123;</span><br><span class="line"> response = contributors;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> delegate.returningResponse(response).contributors(owner, repo);</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">addContributor</span><span class="params">(String owner, String repo, String name, <span class="keyword">int</span> contributions)</span> </span>&#123;</span><br><span class="line"> Map&lt;String, List&lt;Contributor&gt;&gt; repoContributors = ownerRepoContributors.get(owner);</span><br><span class="line"> <span class="keyword">if</span> (repoContributors == <span class="keyword">null</span>) &#123;</span><br><span class="line"> repoContributors = <span class="keyword">new</span> LinkedHashMap&lt;&gt;();</span><br><span class="line"> ownerRepoContributors.put(owner, repoContributors);</span><br><span class="line"> &#125;</span><br><span class="line"> List&lt;Contributor&gt; contributors = repoContributors.get(repo);</span><br><span class="line"> <span class="keyword">if</span> (contributors == <span class="keyword">null</span>) &#123;</span><br><span class="line"> contributors = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"> repoContributors.put(repo, contributors);</span><br><span class="line"> &#125;</span><br><span class="line"> contributors.add(<span class="keyword">new</span> Contributor(name, contributions));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>构建 Mock Server 对象:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create a very simple Retrofit adapter which points the GitHub API.</span></span><br><span class="line">Retrofit retrofit = <span class="keyword">new</span> Retrofit.Builder()</span><br><span class="line"> .baseUrl(SimpleService.API_URL)</span><br><span class="line"> .build();</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Create a MockRetrofit object with a NetworkBehavior which manages the fake behavior of calls.</span></span><br><span class="line">NetworkBehavior behavior = NetworkBehavior.create();</span><br><span class="line">MockRetrofit mockRetrofit = <span class="keyword">new</span> MockRetrofit.Builder(retrofit)</span><br><span class="line"> .networkBehavior(behavior)</span><br><span class="line"> .build();</span><br><span class="line"> </span><br><span class="line">BehaviorDelegate&lt;GitHub&gt; delegate = mockRetrofit.create(GitHub.class);</span><br><span class="line">MockGitHub gitHub = <span class="keyword">new</span> MockGitHub(delegate);</span><br></pre></td></tr></table></figure><ul><li>使用 Mock Server :</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">Call&lt;List&lt;Contributor&gt;&gt; contributors = gitHub.contributors(owner, repo);</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>也就是说,我们完全可以自己造一个假的数据源,通过 Mock Server 来返回这些写数据。</p><p>那么问题来了,这其实并没有完全模拟网络请求的解析流程,如果我只能提供原始的 json 字符串,怎么通过 Retrofit 来实现 Mock Server?</p><p>时间已经不早啦,我就不猥琐发育了,直接推塔~</p><p>本文前面一直专注于介绍 Retrofit,很少提及 OkHttp,殊不知 OkHttp 有一套拦截器的机制,也就是说,我们可以任性的检查 Retrofit 即将发出或者正在发出的所有请求,并且篡改它。所以我们只需要找到我们想要的接口,定制自己的返回结果就好了,下面是一段示例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">OkHttpClient client = <span class="keyword">new</span> OkHttpClient.Builder().addInterceptor(<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"> Response response = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">if</span>(BuildConfig.DEBUG &amp;&amp; chain.request().url().uri().getPath().equals(<span class="string">"/contributors"</span>)) &#123;</span><br><span class="line"> <span class="comment">//这里读取我们需要返回的 Json 字符串</span></span><br><span class="line"> String responseString = ...;</span><br><span class="line"> </span><br><span class="line"> response = <span class="keyword">new</span> Response.Builder()</span><br><span class="line"> .code(<span class="number">200</span>)</span><br><span class="line"> .message(responseString)</span><br><span class="line"> .request(chain.request())</span><br><span class="line"> .protocol(Protocol.HTTP_1_0)</span><br><span class="line"> .body(ResponseBody.create(MediaType.parse(<span class="string">"application/json"</span>), responseString.getBytes()))</span><br><span class="line"> .addHeader(<span class="string">"content-type"</span>, <span class="string">"application/json"</span>)</span><br><span class="line"> .build();</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> response = chain.proceed(chain.request());</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;).build();</span><br><span class="line"> </span><br><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"> .addConverterFactory(GsonConverterFactory.create())</span><br><span class="line"> .client(client)</span><br><span class="line"> .build();</span><br></pre></td></tr></table></figure></p><p>这样,我们就会拦截 contributors 这个 api 并定制其返回了。</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>Retrofit 是非常强大的,本文通过丰富的示例和对源码的挖掘,向大家展示了 Retrofit 自身强大的功能以及扩展性,就算它本身功能不能满足你的需求,你也可以很容易的进行改造,毕竟人家的代码真是写的漂亮啊。</p>]]></content>
<categories>
<category> 博客 </category>
<category> Retrofit </category>
</categories>
<tags>
<tag> 博客 </tag>
<tag> Retrofit </tag>
</tags>
</entry>
<entry>
<title>Android Sensor框架Framework层解读</title>
<link href="/2016/09/15/blogs/SensorFramework/"/>
<url>/2016/09/15/blogs/SensorFramework/</url>
<content type="html"><![CDATA[<h2 id="Sensor整体架构:"><a href="#Sensor整体架构:" class="headerlink" title="Sensor整体架构:"></a>Sensor整体架构:</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/1.png"></p><h3 id="整体架构说明"><a href="#整体架构说明" class="headerlink" title="整体架构说明:"></a>整体架构说明:</h3><ol><li>黄色部分表示硬件,它要挂在I2C总线上 </li><li>红色部分表示驱动,驱动注册到Kernel的Input Subsystem上,然后通过Event Device把Sensor数据传到HAL层,准确说是HAL从Event读</li><li>绿色部分表示动态库,它封装了整个Sensor的IPC机制,如SensorManager是客户端,SensorService是服务端,而HAL部分是封装了服务端对Kernel的直接访问</li><li>蓝色部分就是我们的Framework和Application了,JNI负责访问Sensor的客户端,而Application就是具体的应用程序,用来接收Sensor返回的数据,并处理实现对应的UI效果,如屏幕旋转,打电话时灭屏,自动调接背光(这三个功能的具体实现会在以后分析)</li></ol><h3 id="Sensor总体调用关系图"><a href="#Sensor总体调用关系图" class="headerlink" title="Sensor总体调用关系图"></a>Sensor总体调用关系图</h3><ul><li>本节主要解读Android的Framework层框架。</li><li>Sensor 框架分为三个层次,客户度、服务端、HAL层,服务端负责从HAL读取数据,并将数据写到管道中,客户端通过管道读取服务端数据。</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/2.png"></p><h3 id="客户端主要类"><a href="#客户端主要类" class="headerlink" title="客户端主要类"></a>客户端主要类</h3><ul><li><code>SensorManager.java</code>:从android4.1开始,把SensorManager定义为一个抽象类,定义了一些主要的方法,该类主要是应用层直接使用的类,提供给应用层的接口</li><li><code>SystemSensorManager.java</code>:继承于SensorManager,客户端消息处理的实体,应用程序通过获取其实例,并注册监听接口,获取sensor数据。</li><li><code>sensorEventListener</code>接口:用于注册监听的接口</li><li><code>sensorThread</code>:是SystemSensorManager的一个内部类,开启一个新线程负责读取读取sensor数据,当注册了sensorEventListener接口的时候才会启动线程</li><li><code>android_hardware_SensorManager.cpp</code>:负责与java层通信的JNI接口</li><li><code>SensorManager.cpp</code>:sensor在Native层的客户端,负责与服务端SensorService.cpp的通信</li><li><code>SenorEventQueue.cpp</code>:消息队列</li></ul><h3 id="服务端主要类"><a href="#服务端主要类" class="headerlink" title="服务端主要类"></a>服务端主要类</h3><ul><li><code>SensorService.cpp</code>:服务端数据处理中心</li><li><code>SensorEventConnection</code>:从BnSensorEventConnection继承来,实现接口ISensorEventConnection的一些方法,ISensorEventConnection在SensorEventQueue会保存一个指针,指向调用服务接口创建的SensorEventConnection对象</li><li><code>Bittube.cpp</code>:在这个类中创建了管道,用于服务端与客户端读写数据</li><li><code>SensorDevice</code>:负责与HAL读取数据</li></ul><h3 id="HAL层"><a href="#HAL层" class="headerlink" title="HAL层"></a>HAL层</h3><ul><li><code>Sensor.h</code>是google为Sensor定义的Hal接口,单独提出去</li></ul><h2 id="客户端读取数据"><a href="#客户端读取数据" class="headerlink" title="客户端读取数据"></a>客户端读取数据</h2><h3 id="调用时序图"><a href="#调用时序图" class="headerlink" title="调用时序图"></a>调用时序图</h3><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/3.jpg"></p><h3 id="apk注册监听器"><a href="#apk注册监听器" class="headerlink" title="apk注册监听器"></a>apk注册监听器</h3><ul><li>Activity实现了SensorEventListener接口。在onCreate方法中,获取SystemSensorManager,并获取到加速传感器的Sensor;在onResume方法中调用SystemSensorManager,registerListenerImpl注册监听器;当Sensor数据有改变的时候将会回调onSensorChanged方法。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">SensorManager mSensorManager =</span><br><span class="line"> (SensorManager)getSystemService(SENSOR_SERVICE);</span><br><span class="line"> Sensor mAccelerometer =</span><br><span class="line"> mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);</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">onResume</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onResume();</span><br><span class="line"> mSensorManager. registerListenerImpl (<span class="keyword">this</span>, mAccelerometer,</span><br><span class="line"> SensorManager.SENSOR_DELAY_NORMAL);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onPause</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onPause();</span><br><span class="line"> mSensorManager.unregisterListener(<span class="keyword">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">SensorEventListener</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onSensorChanged</span><span class="params">(SensorEvent event)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAccuracyChanged</span><span class="params">(Sensor sensor, <span class="keyword">int</span> accuracy)</span></span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="初始化SystemSensorManager"><a href="#初始化SystemSensorManager" class="headerlink" title="初始化SystemSensorManager"></a>初始化SystemSensorManager</h3><ul><li><p>系统开机启动的时候,会创建SystemSensorManager的实例,在其构造函数中,主要做了四件事情:</p><ol><li>初始化JNI:调用JNI函数nativeClassInit()进行初始化</li><li>初始化Sensor列表:调用JNI函数sensors_module_init,对Sensor模块进行初始化。创建了native层SensorManager的实例。</li><li>获取Sensor列表:调用JNI函数sensors_module_get_next_sensor()获取Sensor,并存在sHandleToSensor列表中</li><li>构造SensorThread类:构造线程的类函数,并没有启动线程,当有应用注册的时候才会启动线程</li></ol></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">SystemSensorManager</span><span class="params">(Context context,Looper mainLooper)</span> </span>&#123;</span><br><span class="line"> mMainLooper = mainLooper; </span><br><span class="line"> mContext = context;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span>(sListeners) &#123;</span><br><span class="line"> <span class="keyword">if</span> (!sSensorModuleInitialized) &#123;</span><br><span class="line"> sSensorModuleInitialized = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line"> nativeClassInit();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// initialize the sensor list</span></span><br><span class="line"> sensors_module_init();</span><br><span class="line"> <span class="keyword">final</span> ArrayList&lt;Sensor&gt; fullList = sFullSensorsList;</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">do</span> &#123;</span><br><span class="line"> Sensor sensor = <span class="keyword">new</span> Sensor();</span><br><span class="line"> i = sensors_module_get_next_sensor(sensor, i);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (i&gt;=<span class="number">0</span>) &#123;</span><br><span class="line"> <span class="comment">//Log.d(TAG, "found sensor: " + sensor.getName() +</span></span><br><span class="line"> <span class="comment">// ", handle=" + sensor.getHandle());</span></span><br><span class="line"> fullList.add(sensor);</span><br><span class="line"> sHandleToSensor.append(sensor.getHandle(), sensor);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">while</span> (i&gt;<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> sPool = <span class="keyword">new</span> SensorEventPool( sFullSensorsList.size()*<span class="number">2</span> );</span><br><span class="line"> sSensorThread = <span class="keyword">new</span> SensorThread();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><h3 id="启动SensorThread线程读取消息队列中数据"><a href="#启动SensorThread线程读取消息队列中数据" class="headerlink" title="启动SensorThread线程读取消息队列中数据"></a>启动SensorThread线程读取消息队列中数据</h3><ul><li>当有应用程序调用registerListenerImpl()方法注册监听的时候,会调用SensorThread.startLoacked()启动线程。线程只会启动一次,并调用enableSensorLocked()接口对指定的sensor使能,并设置采样时间。  SensorThreadRunnable实现了Runnable接口,在SensorThread类中被启动。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">registerListenerImpl</span><span class="params">(SensorEventListener listener, Sensor sensor,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> delay, Handler handler)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span> (sListeners) &#123;</span><br><span class="line"> ListenerDelegate l = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">for</span> (ListenerDelegate i : sListeners) &#123;</span><br><span class="line"> <span class="keyword">if</span> (i.getListener() == listener) &#123;</span><br><span class="line"> l = i;</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">// if we don't find it, add it to the list</span></span><br><span class="line"> <span class="keyword">if</span> (l == <span class="keyword">null</span>) &#123;</span><br><span class="line"> l = <span class="keyword">new</span> ListenerDelegate(listener, sensor, handler);</span><br><span class="line"> sListeners.add(l);</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">if</span> (sSensorThread.startLocked()) &#123;</span><br><span class="line"> <span class="keyword">if</span> (!enableSensorLocked(sensor, delay)) &#123;</span><br><span class="line"> …….</span><br><span class="line"> &#125;</span><br><span class="line"> ……</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!l.hasSensor(sensor)) &#123;</span><br><span class="line"> l.addSensor(sensor);</span><br><span class="line"> <span class="keyword">if</span> (!enableSensorLocked(sensor, delay)) &#123;</span><br><span class="line"> ……</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><ul><li>在open函数中调用JNI函数sensors_create_queue()来创建消息队列,然后调用SensorManager. createEventQueue()创建。在startLocked函数中启动新的线程后,做了一个while的等待while (mSensorsReady == false),只有当mSensorsReady等于true的时候,才会执行enableSensorLocked()函数对sensor使能。而mSensorsReady变量,是在open()调用创建消息队列成功之后才会true,所以认为,三个功能调用顺序是如下:<ol><li>调用open函数创建消息队列</li><li>调用enableSensorLocked()函数对sensor使能</li><li>调用sensors_data_poll从消息队列中读取数据,而且是在while (true)循环里一直读取</li></ol></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">startLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (mThread == <span class="keyword">null</span>) &#123;</span><br><span class="line"> SensorThreadRunnable runnable = <span class="keyword">new</span> SensorThreadRunnable();</span><br><span class="line"> Thread thread = <span class="keyword">new</span> Thread(runnable, SensorThread.class.getName());</span><br><span class="line"> thread.start();</span><br><span class="line"> <span class="keyword">synchronized</span> (runnable) &#123; <span class="comment">//队列创建成功,线程同步</span></span><br><span class="line"> <span class="keyword">while</span> (mSensorsReady == <span class="keyword">false</span>) &#123;</span><br><span class="line"> runnable.wait();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> &#125;</span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">SensorThreadRunnable</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line"> SensorThreadRunnable() &#123;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">open</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> sQueue = sensors_create_queue();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> …….</span><br><span class="line"> <span class="keyword">if</span> (!open()) &#123;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line"> mSensorsReady = <span class="keyword">true</span>;</span><br><span class="line"> <span class="keyword">this</span>.notify();</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> sensor = sensors_data_poll(sQueue, values, status, timestamp);</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><h2 id="服务端实现"><a href="#服务端实现" class="headerlink" title="服务端实现"></a>服务端实现</h2><h3 id="调用时序图-1"><a href="#调用时序图-1" class="headerlink" title="调用时序图"></a>调用时序图</h3><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/4.png"></p><h3 id="启动SensorService服务"><a href="#启动SensorService服务" class="headerlink" title="启动SensorService服务"></a>启动SensorService服务</h3><ul><li>在SystemServer进程中的main函数中,通过JNI调用,调用到<code>com_android_server_SystemServer.cpp</code>的<code>android_server_SystemServer_init1()</code>方法,该方法又调用<code>system_init.cpp中的system_init()</code>,在这里创建了SensorService的实例。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> <span class="keyword">status_t</span> system_init()</span><br><span class="line">&#123;</span><br><span class="line"> ……</span><br><span class="line"> property_get(<span class="string">"system_init.startsensorservice"</span>, propBuf, <span class="string">"1"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(propBuf, <span class="string">"1"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="comment">// Start the sensor service</span></span><br><span class="line"> SensorService::instantiate();</span><br><span class="line"> &#125;</span><br><span class="line"> …..</span><br><span class="line"> <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="SensorService初始化"><a href="#SensorService初始化" class="headerlink" title="SensorService初始化"></a>SensorService初始化</h3><ul><li>SensorService创建完之后,将会调用SensorService::onFirstRef()方法,在该方法中完成初始化工作。首先获取SensorDevice实例,在其构造函数中,完成了对Sensor模块HAL的初始化:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">SensorDevice::SensorDevice()</span><br><span class="line"> : mSensorDevice(<span class="number">0</span>),</span><br><span class="line"> mSensorModule(<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">status_t</span> err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,</span><br><span class="line"> (<span class="keyword">hw_module_t</span> <span class="keyword">const</span>**)&amp;mSensorModule);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mSensorModule) &#123;</span><br><span class="line"> err = sensors_open(&amp;mSensorModule-&gt;common, &amp;mSensorDevice);</span><br><span class="line"></span><br><span class="line"> ALOGE_IF(err, <span class="string">"couldn't open device for module %s (%s)"</span>,</span><br><span class="line"> SENSORS_HARDWARE_MODULE_ID, strerror(-err));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (mSensorDevice) &#123;</span><br><span class="line"> <span class="keyword">sensor_t</span> <span class="keyword">const</span>* <span class="built_in">list</span>;</span><br><span class="line"> <span class="keyword">ssize_t</span> count = mSensorModule-&gt;get_sensors_list(mSensorModule, &amp;<span class="built_in">list</span>);</span><br><span class="line"> mActivationCount.setCapacity(count);</span><br><span class="line"> Info model;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span> ; i&lt;<span class="keyword">size_t</span>(count) ; i++) &#123;</span><br><span class="line"> mActivationCount.add(<span class="built_in">list</span>[i].handle, model);</span><br><span class="line"> mSensorDevice-&gt;activate(mSensorDevice, <span class="built_in">list</span>[i].handle, <span class="number">0</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>这里主要做了三个工作:</p><ol><li>调用HAL层的hw_get_modele()方法,加载Sensor模块so文件</li><li>调用sensor.h的sensors_open方法打开设备</li><li>调用sensors_poll_device_t-&gt;activate()对Sensor模块使能</li></ol></li><li><p>再看看SensorService::onFirstRef()方法:</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> SensorService::onFirstRef()</span><br><span class="line">&#123;</span><br><span class="line"> SensorDevice&amp; dev(SensorDevice::getInstance());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (dev.initCheck() == NO_ERROR) &#123;</span><br><span class="line"> <span class="keyword">sensor_t</span> <span class="keyword">const</span>* <span class="built_in">list</span>;</span><br><span class="line"> <span class="keyword">ssize_t</span> count = dev.getSensorList(&amp;<span class="built_in">list</span>);</span><br><span class="line"> <span class="keyword">if</span> (count &gt; <span class="number">0</span>) &#123;</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">ssize_t</span> i=<span class="number">0</span> ; i&lt;count ; i++) &#123;</span><br><span class="line"> registerSensor( <span class="keyword">new</span> HardwareSensor(<span class="built_in">list</span>[i]) );</span><br><span class="line"> ……</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// it's safe to instantiate the SensorFusion object here</span></span><br><span class="line"> <span class="comment">// (it wants to be instantiated after h/w sensors have been</span></span><br><span class="line"> <span class="comment">// registered)</span></span><br><span class="line"> const SensorFusion&amp; fusion(SensorFusion::getInstance());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (hasGyro) &#123;</span><br><span class="line"> ……</span><br><span class="line"> &#125;</span><br><span class="line"> ……</span><br><span class="line"> run(<span class="string">"SensorService"</span>, PRIORITY_URGENT_DISPLAY);</span><br><span class="line"> mInitCheck = NO_ERROR;</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><ul><li><p>在这个方法中,主要做了4件事情:</p><ol><li>创建SensorDevice实例</li><li>获取Sensor列表</li><li>调用SensorDevice.getSensorList(),获取Sensor模块所有传感器列表</li><li>为每个传感器注册监听器</li></ol></li><li><p><code>registerSensor(new HardwareSensor(list[i]))</code>;</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">void</span> SensorService::registerSensor(SensorInterface* s)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">sensors_event_t</span> event;</span><br><span class="line"> <span class="built_in">memset</span>(&amp;event, <span class="number">0</span>, <span class="keyword">sizeof</span>(event));</span><br><span class="line"></span><br><span class="line"> const Sensor sensor(s-&gt;getSensor());</span><br><span class="line"> <span class="comment">// 添加到Sensor列表,给客户端使用</span></span><br><span class="line"> mSensorList.add(sensor);</span><br><span class="line"> <span class="comment">// add to our handle-&gt;SensorInterface mapping</span></span><br><span class="line"> mSensorMap.add(sensor.getHandle(), s);</span><br><span class="line"> <span class="comment">// create an entry in the mLastEventSeen array</span></span><br><span class="line"> mLastEventSeen.add(sensor.getHandle(), event);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>HardwareSensor实现了SensorInterface接口。启动线程读取数据,调用run方法启动新线程,将调用SensorService::threadLoop()方法。</li></ul><h3 id="在新的线程中读取HAL层数据"><a href="#在新的线程中读取HAL层数据" class="headerlink" title="在新的线程中读取HAL层数据"></a>在新的线程中读取HAL层数据</h3><ul><li>SensorService实现了Thread类,当在onFirstRef中调用run方法后,将在新的线程中调用<code>SensorService::threadLoop()</code>方法。在while循环中一直读取HAL层数据,再调用<code>SensorEventConnection-&gt;sendEvents</code>将数据写到管道中。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">bool</span> SensorService::threadLoop()</span><br><span class="line">&#123;</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">do</span> &#123;</span><br><span class="line"> count = device.poll(buffer, numEventMax);</span><br><span class="line"></span><br><span class="line"> recordLastValue(buffer, count);</span><br><span class="line"> ……</span><br><span class="line"></span><br><span class="line"> <span class="comment">// send our events to clients...</span></span><br><span class="line"> <span class="keyword">const</span> SortedVector&lt; wp&lt;SensorEventConnection&gt; &gt; activeConnections(</span><br><span class="line"> getActiveConnections());</span><br><span class="line"> <span class="keyword">size_t</span> numConnections = activeConnections.size();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span> ; i&lt;numConnections ; i++) &#123;</span><br><span class="line"> sp&lt;SensorEventConnection&gt; connection(</span><br><span class="line"> activeConnections[i].promote());</span><br><span class="line"> <span class="keyword">if</span> (connection != <span class="number">0</span>) &#123;</span><br><span class="line"> connection-&gt;sendEvents(buffer, count, scratch);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">while</span> (count &gt;= <span class="number">0</span> || Thread::exitPending());</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="客户端与服务端通信"><a href="#客户端与服务端通信" class="headerlink" title="客户端与服务端通信"></a>客户端与服务端通信</h2><h3 id="数据传送"><a href="#数据传送" class="headerlink" title="数据传送"></a>数据传送</h3><ul><li>客户端与服务端通信的状态图:</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/5.png"></p><ul><li>客户端服务端线程,在图中可以看到有两个线程:<ol><li>一个是服务端的一个线程,这个线程负责源源不断的从HAL读取数据。</li><li>另一个是客户端的一个线程,客户端线程负责从消息队列中读数据。</li></ol></li><li>创建消息队列,客户端可以创建多个消息队列,一个消息队列对应有一个与服务器通信的连接接口</li><li>创建连接接口,服务端与客户端沟通的桥梁,服务端读取到HAL层数据后,会扫面有多少个与客户端连接的接口,然后往每个接口的管道中写数据</li><li>创建管道,每一个连接接口都有对应的一个管道。</li><li>上面是设计者设计数据传送的原理,但是目前Android上面的数据传送不能完全按照上面的理解。因为在实际使用中,消息队列只会创建一个,也就是说客户端与服务端之间的通信只有一个连接接口,只有一个管道传数据。那么数据的形式是怎么从HAL层传到JAVA层的呢?其实数据是以一个结构体sensors_event_t的形式从HAL层传到JNI层。看看HAL的sensors_event_t结构体:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">sensors_event_t</span> &#123;</span></span><br><span class="line"> <span class="keyword">int32_t</span> version;</span><br><span class="line"> <span class="keyword">int32_t</span> sensor; <span class="comment">//标识符</span></span><br><span class="line"> <span class="keyword">int32_t</span> type; <span class="comment">//传感器类型</span></span><br><span class="line"> <span class="keyword">int32_t</span> reserved0;</span><br><span class="line"> <span class="keyword">int64_t</span> timestamp; <span class="comment">//时间戳</span></span><br><span class="line"> <span class="keyword">union</span> &#123;</span><br><span class="line"> <span class="keyword">float</span> data[<span class="number">16</span>];</span><br><span class="line"> <span class="keyword">sensors_vec_t</span> acceleration; <span class="comment">//加速度</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> magnetic; <span class="comment">//磁矢量</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> orientation; <span class="comment">//方向</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> gyro; <span class="comment">//陀螺仪</span></span><br><span class="line"> <span class="keyword">float</span> temperature; <span class="comment">//温度</span></span><br><span class="line"> <span class="keyword">float</span> distance; <span class="comment">//距离</span></span><br><span class="line"> <span class="keyword">float</span> light; <span class="comment">//光照</span></span><br><span class="line"> <span class="keyword">float</span> pressure; <span class="comment">//压力</span></span><br><span class="line"> <span class="keyword">float</span> relative_humidity; <span class="comment">//相对湿度</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">uint32_t</span> reserved1[<span class="number">4</span>];</span><br><span class="line">&#125; <span class="keyword">sensors_event_t</span>;</span><br></pre></td></tr></table></figure><ul><li>在JNI层有一个ASensorEvent结构体与sensors_event_t向对应,frameworks/native/include/android/sensor.h:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">ASensorEvent</span> &#123;</span></span><br><span class="line"> <span class="keyword">int32_t</span> version;</span><br><span class="line"> <span class="keyword">int32_t</span> sensor;</span><br><span class="line"> <span class="keyword">int32_t</span> type;</span><br><span class="line"> <span class="keyword">int32_t</span> reserved0;</span><br><span class="line"> <span class="keyword">int64_t</span> timestamp;</span><br><span class="line"> <span class="keyword">union</span> &#123;</span><br><span class="line"> <span class="keyword">float</span> data[<span class="number">16</span>];</span><br><span class="line"> ASensorVector <span class="built_in">vector</span>;</span><br><span class="line"> ASensorVector acceleration;</span><br><span class="line"> ASensorVector magnetic;</span><br><span class="line"> <span class="keyword">float</span> temperature;</span><br><span class="line"> <span class="keyword">float</span> distance;</span><br><span class="line"> <span class="keyword">float</span> light;</span><br><span class="line"> <span class="keyword">float</span> pressure;</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">int32_t</span> reserved1[<span class="number">4</span>];</span><br><span class="line">&#125; ASensorEvent;</span><br></pre></td></tr></table></figure><h2 id="交互调用时序图"><a href="#交互调用时序图" class="headerlink" title="交互调用时序图"></a>交互调用时序图</h2><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/6.jpg"></p><ul><li>经过前面的介绍,现在知道了客户端实现的方式及服务端的实现,但是没有具体讲到它们是如何进行通信的,现在看看客户端与服务端之间的通信。</li><li>主要涉及的是进程间通信,有IBind和管道通信。</li><li>客户端通过IBind通信获取到服务端的远程调用,然后通过管道进行sensor数据的传输。</li></ul><h3 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h3><ul><li>native层实现了sensor服务的核心实现,Sensor服务的主要流程的实现在sensorservice类中,下面重点分析下这个类的流程。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SensorService</span> :</span></span><br><span class="line"> <span class="keyword">public</span> BinderService&lt;SensorService&gt;,</span><br><span class="line"> <span class="keyword">public</span> BnSensorServer,</span><br><span class="line"> <span class="keyword">protected</span> Thread</span><br></pre></td></tr></table></figure><ul><li>看看sensorService继承的类:继承BinderService<sensorservice>这个模板类添加到系统服务,用于Ibinder进程间通信。</sensorservice></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">template</span>&lt;<span class="keyword">typename</span> SERVICE&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BinderService</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">static</span> status_t <span class="title">publish</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> sp&lt;IServiceManager&gt; sm(defaultServiceManager());</span><br><span class="line"> <span class="keyword">return</span> sm-&gt;addService(String16(SERVICE::getServiceName()), <span class="keyword">new</span> SERVICE());</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">publishAndJoinThreadPool</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> sp&lt;ProcessState&gt; proc(ProcessState::self());</span><br><span class="line"> sp&lt;IServiceManager&gt; sm(defaultServiceManager());</span><br><span class="line"> sm-&gt;addService(String16(SERVICE::getServiceName()), <span class="keyword">new</span> SERVICE());</span><br><span class="line"> ProcessState::self()-&gt;startThreadPool();</span><br><span class="line"> IPCThreadState::self()-&gt;joinThreadPool();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">instantiate</span><span class="params">()</span> </span>&#123; publish(); &#125;</span><br><span class="line">&#125;;</span><br><span class="line">&#125;; <span class="comment">// namespace android</span></span><br></pre></td></tr></table></figure><ul><li><p>在前面的介绍中,SensorService服务的实例是在System_init.cpp中调用SensorService::instantiate()创建的,即调用了上面的instantiate()方法,接着调用了publish(),在该方法中,我们看到了new SensorService的实例,并且调用了defaultServiceManager::addService()将Sensor服务添加到了系统服务管理中,客户端可以通过defaultServiceManager:getService()获取到Sensor服务的实例。</p></li><li><p>继承BnSensorServer这个是sensor服务抽象接口类提供给客户端调用:</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="class"><span class="keyword">class</span> <span class="title">Sensor</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ISensorEventConnection</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ISensorServer</span> :</span> <span class="keyword">public</span> IInterface</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> DECLARE_META_INTERFACE(SensorServer);</span><br><span class="line"> <span class="comment">//获取Sensor列表</span></span><br><span class="line"><span class="keyword">virtual</span> Vector&lt;Sensor&gt; getSensorList() = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//创建一个连接的接口,这些都是提供给客户端的抽象接口,服务端实例化时候必须实现</span></span><br><span class="line"> <span class="keyword">virtual</span> sp&lt;ISensorEventConnection&gt; createSensorEventConnection() = <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BnSensorServer</span> :</span> <span class="keyword">public</span> BnInterface&lt;ISensorServer&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">//传输打包数据的通讯接口,在BnSensorServer被实现</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> status_t <span class="title">onTransact</span><span class="params">( <span class="keyword">uint32_t</span> code,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> Parcel&amp; data,</span></span></span><br><span class="line"><span class="function"><span class="params"> Parcel* reply,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">uint32_t</span> flags = <span class="number">0</span>)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line">&#125;; <span class="comment">// namespace android</span></span><br></pre></td></tr></table></figure><ul><li>ISensorServer接口提供了两个抽象方法给客户端调用,关键在于createSensorEventConnection()方法,该在服务端被实现,在客户端被调用,并返回一个SensorEventConnection的实例,创建连接,客户端拿到SensorEventConnection实例之后,可以对sensor进行通信操作,仅仅作为通信的接口而已,它并没有用来传送Sensor数据,因为Sensor数据量比较大,IBind实现比较困难。真正实现Sensor数据传送的是管道,在创建SensorEventConnection实例中,创建了BitTube对象,里面创建了管道,用于客户端与服务端的通信。</li></ul><h3 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h3><ul><li>时序图:</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/7.png"></p><ul><li>客户端主要在SensorManager.cpp中创建消息队列:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ISensorEventConnection</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sensor</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Looper</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ----------------------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SensorEventQueue</span> :</span> <span class="keyword">public</span> ASensorEventQueue, <span class="keyword">public</span> RefBase</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> SensorEventQueue(<span class="keyword">const</span> sp&lt;ISensorEventConnection&gt;&amp; connection);</span><br><span class="line"> <span class="keyword">virtual</span> ~SensorEventQueue();</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">onFirstRef</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="comment">//获取管道句柄</span></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">getFd</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line"> <span class="comment">//向管道写数据</span></span><br><span class="line"> <span class="function"><span class="keyword">static</span> ssize_t <span class="title">write</span><span class="params">(<span class="keyword">const</span> sp&lt;BitTube&gt;&amp; tube,</span></span></span><br><span class="line"><span class="function"><span class="params"> ASensorEvent <span class="keyword">const</span>* events, <span class="keyword">size_t</span> numEvents)</span></span>;</span><br><span class="line"> <span class="comment">//向管道读数据</span></span><br><span class="line"> <span class="keyword">ssize_t</span> read(ASensorEvent* events, <span class="keyword">size_t</span> numEvents);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">status_t</span> waitForEvent() <span class="keyword">const</span>;</span><br><span class="line"> <span class="keyword">status_t</span> wake() <span class="keyword">const</span>;</span><br><span class="line"> <span class="comment">//使能Sensor传感器</span></span><br><span class="line"> <span class="keyword">status_t</span> enableSensor(Sensor <span class="keyword">const</span>* sensor) <span class="keyword">const</span>;</span><br><span class="line"> <span class="keyword">status_t</span> disableSensor(Sensor <span class="keyword">const</span>* sensor) <span class="keyword">const</span>;</span><br><span class="line"> <span class="keyword">status_t</span> setEventRate(Sensor <span class="keyword">const</span>* sensor, <span class="keyword">nsecs_t</span> ns) <span class="keyword">const</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// these are here only to support SensorManager.java</span></span><br><span class="line"> <span class="keyword">status_t</span> enableSensor(<span class="keyword">int32_t</span> handle, <span class="keyword">int32_t</span> us) <span class="keyword">const</span>;</span><br><span class="line"> <span class="keyword">status_t</span> disableSensor(<span class="keyword">int32_t</span> handle) <span class="keyword">const</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">sp&lt;Looper&gt; getLooper() <span class="keyword">const</span>;</span><br><span class="line"><span class="comment">//连接接口,在SensorService中创建的</span></span><br><span class="line">sp&lt;ISensorEventConnection&gt; mSensorEventConnection;</span><br><span class="line"><span class="comment">//管道指针</span></span><br><span class="line"> sp&lt;BitTube&gt; mSensorChannel;</span><br><span class="line"> <span class="keyword">mutable</span> Mutex mLock;</span><br><span class="line"> <span class="keyword">mutable</span> sp&lt;Looper&gt; mLooper;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><ul><li>SensorEventQueue类作为消息队列,作用非常重要,在创建其实例的时候,传入了SensorEventConnection的实例,SensorEventConnection继承于ISensorEventConnection。SensorEventConnection其实是客户端调用SensorService的createSensorEventConnection()方法创建的,它是客户端与服务端沟通的桥梁,通过这个桥梁,可以完成一下任务:<ol><li>获取管道的句柄</li><li>往管道读写数据</li><li>通知服务端对Sensor使能</li></ol></li></ul><h2 id="流程解析"><a href="#流程解析" class="headerlink" title="流程解析"></a>流程解析</h2><h3 id="客户端获取SensorService服务实例"><a href="#客户端获取SensorService服务实例" class="headerlink" title="客户端获取SensorService服务实例"></a>客户端获取SensorService服务实例</h3><ul><li>客户端初始化的时候,即SystemSensorManager的构造函数中,通过JNI调用,创建native层SensorManager的实例,然后调用SensorManager::assertStateLocked()方法做一些初始化的动作。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">status_t</span> SensorManager::assertStateLocked() <span class="keyword">const</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (mSensorServer == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="comment">// try for one second</span></span><br><span class="line"> <span class="function"><span class="keyword">const</span> String16 <span class="title">name</span><span class="params">(<span class="string">"sensorservice"</span>)</span></span>;</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">status_t</span> err = getService(name, &amp;mSensorServer);</span><br><span class="line"> ……</span><br><span class="line"> mSensors = mSensorServer-&gt;getSensorList();</span><br><span class="line"> <span class="keyword">size_t</span> count = mSensors.size();</span><br><span class="line"> mSensorList = (Sensor <span class="keyword">const</span>**)<span class="built_in">malloc</span>(count * <span class="keyword">sizeof</span>(Sensor*));</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span> ; i&lt;count ; i++) &#123;</span><br><span class="line"> mSensorList[i] = mSensors.<span class="built_in">array</span>() + i;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>前面我们讲到过,SensorService的创建的时候调用了defaultServiceManager:getService()将服务添加到了系统服务管理中。现在我们又调用defaultServiceManager::geService()获取到SensorService服务的实例。在通过IBind通信,就可以获取到Sensor列表,所以在客户端初始化的时候,做了两件事情:<ol><li>获取SensorService实例引用</li><li>获取Sensor传感器列表</li></ol></li></ul><h3 id="注册SensorLisenter"><a href="#注册SensorLisenter" class="headerlink" title="注册SensorLisenter"></a>注册SensorLisenter</h3><ul><li>时序图:</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/8.png"></p><ul><li><code>new ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler)</code>在这个构造函数中会创建一个Handler,它会在获取到Sensor数据的时候被调用。</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></pre></td><td class="code"><pre><span class="line">mHandler = <span class="keyword">new</span> Handler(looper) &#123; </span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123; </span><br><span class="line"> <span class="keyword">final</span> SensorEvent t = (SensorEvent)msg.obj; </span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> handle = t.sensor.getHandle(); </span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (t.sensor.getType()) &#123; </span><br><span class="line"> <span class="comment">// Only report accuracy for sensors that support it. </span></span><br><span class="line"> <span class="keyword">case</span> Sensor.TYPE_MAGNETIC_FIELD: </span><br><span class="line"> <span class="keyword">case</span> Sensor.TYPE_ORIENTATION: </span><br><span class="line"> <span class="comment">// call onAccuracyChanged() only if the value changes </span></span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> accuracy = mSensorAccuracies.get(handle); </span><br><span class="line"> <span class="keyword">if</span> ((t.accuracy &gt;= <span class="number">0</span>) &amp;&amp; (accuracy != t.accuracy)) &#123; </span><br><span class="line"> mSensorAccuracies.put(handle, t.accuracy); </span><br><span class="line"> mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); </span><br><span class="line"> &#125; </span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">default</span>: </span><br><span class="line"> <span class="comment">// For other sensors, just report the accuracy once </span></span><br><span class="line"> <span class="keyword">if</span> (mFirstEvent.get(handle) == <span class="keyword">false</span>) &#123; </span><br><span class="line"> mFirstEvent.put(handle, <span class="keyword">true</span>); </span><br><span class="line"> mSensorEventListener.onAccuracyChanged( </span><br><span class="line"> t.sensor, SENSOR_STATUS_ACCURACY_HIGH); </span><br><span class="line"> &#125; </span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> &#125; </span><br><span class="line"></span><br><span class="line"> mSensorEventListener.onSensorChanged(t); </span><br><span class="line"> sPool.returnToPool(t); </span><br><span class="line"> &#125; </span><br><span class="line"> &#125;;</span><br></pre></td></tr></table></figure><h3 id="创建消息队列"><a href="#创建消息队列" class="headerlink" title="创建消息队列"></a>创建消息队列</h3><ul><li>时序图:</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/9.png"></p><ul><li><p>当客户端第一次注册监听器的时候,就需要创建一个消息队列,也就是说,<strong>android在目前的实现中,只创建了一个消息队列,一个消息队列中有一个管道,用于服务端与客户断传送Sensor数据</strong>。</p></li><li><p>在SensorManager.cpp中的createEventQueue方法创建消息队列:</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">sp&lt;SensorEventQueue&gt; SensorManager::createEventQueue()</span><br><span class="line">&#123;</span><br><span class="line"> sp&lt;SensorEventQueue&gt; <span class="built_in">queue</span>;</span><br><span class="line"> Mutex::Autolock _l(mLock);</span><br><span class="line"><span class="keyword">while</span> (assertStateLocked() == NO_ERROR) &#123;</span><br><span class="line"> <span class="comment">//创建连接接口</span></span><br><span class="line"> sp&lt;ISensorEventConnection&gt; connection =</span><br><span class="line"> mSensorServer-&gt;createSensorEventConnection();</span><br><span class="line"> <span class="keyword">if</span> (connection == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="comment">// SensorService just died.</span></span><br><span class="line"> LOGE(<span class="string">"createEventQueue: connection is NULL. SensorService died."</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">//创建消息队列</span></span><br><span class="line"> <span class="built_in">queue</span> = <span class="keyword">new</span> SensorEventQueue(connection);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">queue</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>客户端与服务器创建一个<code>SensorEventConnection</code>连接接口,<strong>而一个消息队列中包含一个连接接口</strong>。创建连接接口:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">sp&lt;ISensorEventConnection&gt; SensorService::createSensorEventConnection()</span><br><span class="line">&#123;</span><br><span class="line"> sp&lt;SensorEventConnection&gt; result(<span class="keyword">new</span> SensorEventConnection(<span class="keyword">this</span>));</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line">SensorService::SensorEventConnection::SensorEventConnection(</span><br><span class="line"> <span class="keyword">const</span> sp&lt;SensorService&gt;&amp; service)</span><br><span class="line"> : mService(service), mChannel(<span class="keyword">new</span> BitTube ())</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>关键在于BitTube,在构造函数中创建了管道:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">BitTube::BitTube()</span><br><span class="line"> : mSendFd(<span class="number">-1</span>), mReceiveFd(<span class="number">-1</span>)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">int</span> sockets[<span class="number">2</span>];</span><br><span class="line"> <span class="keyword">if</span> (socketpair(AF_UNIX, SOCK_SEQPACKET, <span class="number">0</span>, sockets) == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">int</span> size = SOCKET_BUFFER_SIZE;</span><br><span class="line"> setsockopt(sockets[<span class="number">0</span>], SOL_SOCKET, SO_SNDBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line"> setsockopt(sockets[<span class="number">0</span>], SOL_SOCKET, SO_RCVBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line"> setsockopt(sockets[<span class="number">1</span>], SOL_SOCKET, SO_SNDBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line"> setsockopt(sockets[<span class="number">1</span>], SOL_SOCKET, SO_RCVBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line"> fcntl(sockets[<span class="number">0</span>], F_SETFL, O_NONBLOCK);</span><br><span class="line"> fcntl(sockets[<span class="number">1</span>], F_SETFL, O_NONBLOCK);</span><br><span class="line"> mReceiveFd = sockets[<span class="number">0</span>];</span><br><span class="line"> mSendFd = sockets[<span class="number">1</span>];</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> mReceiveFd = -errno;</span><br><span class="line"> ALOGE(<span class="string">"BitTube: pipe creation failed (%s)"</span>, strerror(-mReceiveFd));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>其中:fds[0]就是对应的mReceiveFd,是管道的读端,sensor数据的读取端,对应的是客户端进程访问的。fds[1]就是对应mSendFd,是管道的写端,sensor数据写入端,是sensor的服务进程访问的一端。通过pipe(fds)创建管道,通过fcntl来设置操作管道的方式,设置通道两端的操作方式为O_NONBLOCK ,非阻塞IO方式,read或write调用返回-1和EAGAIN错误。总结下消息队列,客户端第一次注册监听器的时候,就需要创建一个消息队列,客户端创了SensorThread线程从消息队列里面读取数据。SensorEventQueue中有一个SensorEventConnection实例的引用,SensorEventConnection中有一个BitTube实例的引用。</li></ul><h3 id="使能Sensor"><a href="#使能Sensor" class="headerlink" title="使能Sensor"></a>使能Sensor</h3><ul><li>客户端创建了连接接口SensorEventConnection后,可以调用其方法使能Sensor传感器:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">status_t</span> SensorService::SensorEventConnection::enableDisable(</span><br><span class="line"> <span class="keyword">int</span> handle, <span class="keyword">bool</span> enabled)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">status_t</span> err;</span><br><span class="line"> <span class="keyword">if</span> (enabled) &#123;</span><br><span class="line"> err = mService-&gt;enable(<span class="keyword">this</span>, handle);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> err = mService-&gt;disable(<span class="keyword">this</span>, handle);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> err;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>handle对应着Sensor传感器的句柄</li></ul><h3 id="服务端往管道写数据"><a href="#服务端往管道写数据" class="headerlink" title="服务端往管道写数据"></a>服务端往管道写数据</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">bool</span> SensorService::threadLoop()</span><br><span class="line">&#123;</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">do</span> &#123;</span><br><span class="line"> count = device.poll(buffer, numEventMax);</span><br><span class="line"></span><br><span class="line"> recordLastValue(buffer, count);</span><br><span class="line"> ……</span><br><span class="line"></span><br><span class="line"> <span class="comment">// send our events to clients...</span></span><br><span class="line"> <span class="keyword">const</span> SortedVector&lt; wp&lt;SensorEventConnection&gt; &gt; activeConnections(</span><br><span class="line"> getActiveConnections());</span><br><span class="line"> <span class="keyword">size_t</span> numConnections = activeConnections.size();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span> ; i&lt;numConnections ; i++) &#123;</span><br><span class="line"> sp&lt;SensorEventConnection&gt; connection(</span><br><span class="line"> activeConnections[i].promote());</span><br><span class="line"> <span class="keyword">if</span> (connection != <span class="number">0</span>) &#123;</span><br><span class="line"> connection-&gt;sendEvents(buffer, count, scratch);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">while</span> (count &gt;= <span class="number">0</span> || Thread::exitPending());</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>前面介绍过,在SensorService中,创建了一个线程不断从HAL层读取Sensor数据,就是在threadLoop方法中。</p></li><li><p>关键在与下面了一个for循环,其实是扫描有多少个客户端连接接口,然后就往没每个连接的管道中写数据</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">status_t</span> SensorService::SensorEventConnection::sendEvents(</span><br><span class="line"> <span class="keyword">sensors_event_t</span> <span class="keyword">const</span>* buffer, <span class="keyword">size_t</span> numEvents,</span><br><span class="line"> <span class="keyword">sensors_event_t</span>* scratch)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="comment">// filter out events not for this connection</span></span><br><span class="line"> <span class="keyword">size_t</span> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (scratch) &#123;</span><br><span class="line"> ……</span><br><span class="line"> &#125;</span><br><span class="line"> ……</span><br><span class="line"> <span class="keyword">if</span> (count == <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><br><span class="line"> <span class="keyword">ssize_t</span> size = mChannel-&gt;write(scratch, count*<span class="keyword">sizeof</span>(<span class="keyword">sensors_event_t</span>));</span><br><span class="line"> ……</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>调用该连接接口的BitTube::write(),到此,服务端就完成了往管道的写端写入数据:</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">ssize_t</span> BitTube::write(<span class="keyword">void</span> <span class="keyword">const</span>* vaddr, <span class="keyword">size_t</span> size)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">ssize_t</span> err, len;</span><br><span class="line"> <span class="keyword">do</span> &#123;</span><br><span class="line"> len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);</span><br><span class="line"> err = len &lt; <span class="number">0</span> ? errno : <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">while</span> (err == EINTR);</span><br><span class="line"> <span class="keyword">return</span> err == <span class="number">0</span> ? len : -err;</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><ul><li>时序图:</li></ul><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorFramework/10.png"></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><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">ssize_t</span> SensorEventQueue::read(ASensorEvent* events, <span class="keyword">size_t</span> numEvents)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">return</span> BitTube::recvObjects(mSensorChannel, events, numEvents);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>调用到了BitTube::read():</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">static</span> ssize_t <span class="title">recvObjects</span><span class="params">(<span class="keyword">const</span> sp&lt;BitTube&gt;&amp; tube,</span></span></span><br><span class="line"><span class="function"><span class="params"> T* events, <span class="keyword">size_t</span> count)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> recvObjects(tube, events, count, <span class="keyword">sizeof</span>(T));</span><br><span class="line"> &#125;</span><br><span class="line"><span class="keyword">ssize_t</span> BitTube::recvObjects(<span class="keyword">const</span> sp&lt;BitTube&gt;&amp; tube,</span><br><span class="line"> <span class="keyword">void</span>* events, <span class="keyword">size_t</span> count, <span class="keyword">size_t</span> objSize)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">ssize_t</span> numObjects = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">size_t</span> i=<span class="number">0</span> ; i&lt;count ; i++) &#123;</span><br><span class="line"> <span class="keyword">char</span>* vaddr = <span class="keyword">reinterpret_cast</span>&lt;<span class="keyword">char</span>*&gt;(events) + objSize * i;</span><br><span class="line"> <span class="keyword">ssize_t</span> size = tube-&gt;read(vaddr, objSize);</span><br><span class="line"> <span class="keyword">if</span> (size &lt; <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="comment">// error occurred</span></span><br><span class="line"> <span class="keyword">return</span> size;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (size == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="comment">// no more messages</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> numObjects++;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> numObjects;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">ssize_t</span> BitTube::read(<span class="keyword">void</span>* vaddr, <span class="keyword">size_t</span> size)</span><br><span class="line">&#123;</span><br><span class="line"> <span class="keyword">ssize_t</span> err, len;</span><br><span class="line"> <span class="keyword">do</span> &#123;</span><br><span class="line"> len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);</span><br><span class="line"> err = len &lt; <span class="number">0</span> ? errno : <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">while</span> (err == EINTR);</span><br><span class="line"> <span class="keyword">if</span> (err == EAGAIN || err == EWOULDBLOCK) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> err == <span class="number">0</span> ? len : -err;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 博客 </category>
<category> Sensor </category>
</categories>
<tags>
<tag> 博客 </tag>
<tag> Sensor </tag>
</tags>
</entry>
<entry>
<title>Android Sensor框架HAL层解读</title>
<link href="/2016/09/14/blogs/SensorHAL/"/>
<url>/2016/09/14/blogs/SensorHAL/</url>
<content type="html"><![CDATA[<h2 id="Android-sensor构建"><a href="#Android-sensor构建" class="headerlink" title="Android sensor构建"></a>Android sensor构建</h2><ol><li><p>Android6.0 系统内置对传感器的支持达26种,他们分别是:加速度传感器(accelerometer)、磁力传感器(magnetic field)、方向传感器(orientation)、陀螺仪(gyroscope)、环境光照传感器(light)、压力传感器(pressure)、温度传感器(temperature)和距离传感器(proximity)等。</p></li><li><p>Android实现传感器系统包括以下几个部分:</p><ul><li>java层</li><li>JNI层</li><li>HAL层</li><li>驱动层</li></ul></li><li><p>各部分之间架构图如下:</p></li></ol><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorHAL/1.png"></p><h2 id="Sensor-HAL层接口"><a href="#Sensor-HAL层接口" class="headerlink" title="Sensor HAL层接口"></a>Sensor HAL层接口</h2><ol><li><p>Google为Sensor提供了统一的HAL接口,不同的硬件厂商需要根据该接口来实现并完成具体的硬件抽象层。Android中Sensor的HAL接口定义在:<code>hardware/libhardware/include/hardware/sensors.h</code></p></li><li><p>对传感器类型的定义:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_ACCELEROMETER (1) <span class="comment">//加速度传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_MAGNETIC_FIELD (2) <span class="comment">//磁力传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_ORIENTATION (3) <span class="comment">//方向</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GYROSCOPE (4) <span class="comment">//陀螺仪</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_LIGHT (5) <span class="comment">//环境光照传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_PRESSURE (6) <span class="comment">//压力传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_TEMPERATURE (7) <span class="comment">//温度传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_PROXIMITY (8) <span class="comment">//距离传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GRAVITY (9) <span class="comment">//重力</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_LINEAR_ACCELERATION (10) <span class="comment">//线性加速度</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_ROTATION_VECTOR (11) <span class="comment">//运动</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_RELATIVE_HUMIDITY (12) <span class="comment">//湿度传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_AMBIENT_TEMPERATURE (13) <span class="comment">//环境温度传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED (14) <span class="comment">//未校准磁力传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GAME_ROTATION_VECTOR (15) <span class="comment">//游戏动作传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GYROSCOPE_UNCALIBRATED (16) <span class="comment">//未校准陀螺仪</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_SIGNIFICANT_MOTION (17) <span class="comment">//特殊动作触发传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_STEP_DETECTOR (18) <span class="comment">//步行检测传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_STEP_COUNTER (19) <span class="comment">//计步传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR (20) <span class="comment">//地磁旋转矢量传感器,提供手机的旋转矢量</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_HEART_RATE (21) <span class="comment">//心率传感器</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_TILT_DETECTOR (22) <span class="comment">//每次检测到倾斜事件后均生成事件</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_WAKE_GESTURE (23) <span class="comment">//支持根据设备特定的动作唤醒设备</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_GLANCE_GESTURE (24) <span class="comment">//支持短暂打开屏幕,以便用户根据特定动作浏览屏幕上的内容</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_PICK_UP_GESTURE (25) <span class="comment">//拾起设备时触发,无论面前是什么(桌子、口袋、手提袋)</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SENSOR_TYPE_WRIST_TILT_GESTURE (26) <span class="comment">//腕关节倾斜触发生成事件</span></span></span><br></pre></td></tr></table></figure></li><li><p>传感器模块的定义结构体如下,该接口的定义实际上是对标准的硬件模块hw_module_t的一个扩展,增加了一个<code>get_sensors_list</code>函数,用于获取传感器的列表。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="class"><span class="keyword">struct</span> <span class="title">sensors_module_t</span> &#123;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">hw_module_t</span> <span class="title">common</span>;</span></span><br><span class="line"> <span class="keyword">int</span> (*get_sensors_list)(struct <span class="keyword">sensors_module_t</span>* <span class="keyword">module</span>,</span><br><span class="line"> struct <span class="keyword">sensor_t</span> <span class="keyword">const</span>** <span class="built_in">list</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>对任意一个sensor设备都会有一个<code>sensor_t</code>结构体,其定义如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sensor_t</span> &#123;</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span>* name; <span class="comment">//传感器名字</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span>* vendor;</span><br><span class="line"> <span class="keyword">int</span> version; <span class="comment">//版本</span></span><br><span class="line"> <span class="keyword">int</span> handle; <span class="comment">//传感器的handle句柄</span></span><br><span class="line"> <span class="keyword">int</span> type; <span class="comment">//传感器类型</span></span><br><span class="line"> <span class="keyword">float</span> maxRange; <span class="comment">//最大范围</span></span><br><span class="line"> <span class="keyword">float</span> resolution; <span class="comment">//解析度</span></span><br><span class="line"> <span class="keyword">float</span> power; <span class="comment">//消耗能源</span></span><br><span class="line"> <span class="keyword">int32_t</span> minDelay; <span class="comment">//事件间隔最小时间</span></span><br><span class="line"> <span class="keyword">void</span>* reserved[<span class="number">8</span>]; <span class="comment">//保留字段,必须为0</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>每个传感器的数据由<code>sensors_event_t</code>结构体表示,定义如下,其中,sensor为传感器的标志符,而不同的传感器则采用union方式来表示。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">sensors_event_t</span> &#123;</span></span><br><span class="line"> <span class="keyword">int32_t</span> version;</span><br><span class="line"> <span class="keyword">int32_t</span> sensor; <span class="comment">//标识符</span></span><br><span class="line"> <span class="keyword">int32_t</span> type; <span class="comment">//传感器类型</span></span><br><span class="line"> <span class="keyword">int32_t</span> reserved0;</span><br><span class="line"> <span class="keyword">int64_t</span> timestamp; <span class="comment">//时间戳</span></span><br><span class="line"> <span class="keyword">union</span> &#123;</span><br><span class="line"> <span class="keyword">float</span> data[<span class="number">16</span>];</span><br><span class="line"> <span class="keyword">sensors_vec_t</span> acceleration; <span class="comment">//加速度</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> magnetic; <span class="comment">//磁矢量</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> orientation; <span class="comment">//方向</span></span><br><span class="line"> <span class="keyword">sensors_vec_t</span> gyro; <span class="comment">//陀螺仪</span></span><br><span class="line"> <span class="keyword">float</span> temperature; <span class="comment">//温度</span></span><br><span class="line"> <span class="keyword">float</span> distance; <span class="comment">//距离</span></span><br><span class="line"> <span class="keyword">float</span> light; <span class="comment">//光照</span></span><br><span class="line"> <span class="keyword">float</span> pressure; <span class="comment">//压力</span></span><br><span class="line"> <span class="keyword">float</span> relative_humidity; <span class="comment">//相对湿度</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">uint32_t</span> reserved1[<span class="number">4</span>];</span><br><span class="line">&#125; <span class="keyword">sensors_event_t</span>;</span><br></pre></td></tr></table></figure></li><li><p><code>sensors_vec_t</code>结构体用来表示不同传感器的数据:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line"> <span class="keyword">union</span> &#123;</span><br><span class="line"> <span class="keyword">float</span> v[<span class="number">3</span>];</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line"> <span class="keyword">float</span> x;</span><br><span class="line"> <span class="keyword">float</span> y;</span><br><span class="line"> <span class="keyword">float</span> z;</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line"> <span class="keyword">float</span> azimuth;</span><br><span class="line"> <span class="keyword">float</span> pitch;</span><br><span class="line"> <span class="keyword">float</span> roll;</span><br><span class="line"> &#125;;</span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">int8_t</span> status;</span><br><span class="line"> <span class="keyword">uint8_t</span> reserved[<span class="number">3</span>];</span><br><span class="line">&#125; <span class="keyword">sensors_vec_t</span>;</span><br></pre></td></tr></table></figure></li><li><p>Sensor设备结构体<code>sensors_poll_device_t</code>,对标准硬件设备<code>hw_device_t</code>结构体的扩展,主要完成读取底层数据,并将数据存储在<code>struct sensors_poll_device_t</code>结构体中;poll函数用来获取底层数据,调用时将被阻塞定义如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sensors_poll_device_t</span> &#123;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">hw_device_t</span> <span class="title">common</span>;</span></span><br><span class="line"><span class="comment">//Activate/deactivate one sensor</span></span><br><span class="line"> <span class="keyword">int</span> (*activate)(struct <span class="keyword">sensors_poll_device_t</span> *dev,</span><br><span class="line"> <span class="keyword">int</span> handle, <span class="keyword">int</span> enabled);</span><br><span class="line"> <span class="comment">//Set the delay between sensor events in nanoseconds for a given sensor.</span></span><br><span class="line"> <span class="keyword">int</span> (*setDelay)(struct <span class="keyword">sensors_poll_device_t</span> *dev,</span><br><span class="line"> <span class="keyword">int</span> handle, <span class="keyword">int64_t</span> ns);</span><br><span class="line"> <span class="comment">//获取数据</span></span><br><span class="line"> <span class="keyword">int</span> (*poll)(struct <span class="keyword">sensors_poll_device_t</span> *dev,</span><br><span class="line"> <span class="keyword">sensors_event_t</span>* data, <span class="keyword">int</span> count);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>控制设备打开/关闭结构体定义如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">static</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">sensors_open</span><span class="params">(<span class="keyword">const</span> struct <span class="keyword">hw_module_t</span>* <span class="keyword">module</span>,</span></span></span><br><span class="line"><span class="function"><span class="params"> struct <span class="keyword">sensors_poll_device_t</span>** device)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">module</span>-&gt;methods-&gt;open(<span class="keyword">module</span>,</span><br><span class="line"> SENSORS_HARDWARE_POLL, (struct <span class="keyword">hw_device_t</span>**)device);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">sensors_close</span><span class="params">(struct <span class="keyword">sensors_poll_device_t</span>* device)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> device-&gt;common.close(&amp;device-&gt;common);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h2 id="Sensor-HAL实现"><a href="#Sensor-HAL实现" class="headerlink" title="Sensor HAL实现"></a>Sensor HAL实现</h2><ol><li>打开设备时序图</li></ol><p><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/sensorHAL/2.jpg"></p><ol start="2"><li>SensorDevice属于JNI层,与HAL进行通信的接口,在JNI层调用了HAL层的<code>open_sensors()</code>方法打开设备模块,再调用<code>poll__activate()</code>对设备使能,然后调用<code>poll__poll</code>读取数据。</li></ol>]]></content>
<categories>
<category> 博客 </category>
<category> Sensor </category>
</categories>
<tags>
<tag> 博客 </tag>
<tag> Sensor </tag>
</tags>
</entry>
<entry>
<title>Hexo换了电脑处理方法</title>
<link href="/2016/09/12/notes/HexoForBlog/"/>
<url>/2016/09/12/notes/HexoForBlog/</url>
<content type="html"><![CDATA[<h2 id="本文只讲述hexo换了电脑处理方法,如果想了解使用hexo搭建个人博客,请访问:使用hexo搭建个人博客"><a href="#本文只讲述hexo换了电脑处理方法,如果想了解使用hexo搭建个人博客,请访问:使用hexo搭建个人博客" class="headerlink" title="本文只讲述hexo换了电脑处理方法,如果想了解使用hexo搭建个人博客,请访问:使用hexo搭建个人博客"></a>本文只讲述hexo换了电脑处理方法,如果想了解使用hexo搭建个人博客,请访问:<a href="http://www.jianshu.com/p/73ca570e4d61" target="_blank" rel="noopener">使用hexo搭建个人博客</a></h2><h2 id="为了可以在多个电脑上面都处理hexo博客,所以我把source文件和网站的文件分别放在hexo和master分支上面,首先将hexo分支克隆到本地:"><a href="#为了可以在多个电脑上面都处理hexo博客,所以我把source文件和网站的文件分别放在hexo和master分支上面,首先将hexo分支克隆到本地:" class="headerlink" title="为了可以在多个电脑上面都处理hexo博客,所以我把source文件和网站的文件分别放在hexo和master分支上面,首先将hexo分支克隆到本地:"></a>为了可以在多个电脑上面都处理hexo博客,所以我把source文件和网站的文件分别放在hexo和master分支上面,首先将hexo分支克隆到本地:</h2><figure class="highlight bash"><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">$ git <span class="built_in">clone</span> -b hexo git@github.com:way1989/way1989.github.io.git</span><br><span class="line">$ <span class="built_in">cd</span> way1989.github.io</span><br><span class="line">$ npm install</span><br></pre></td></tr></table></figure><h2 id="此时,你的博客就OK了,运行-hexo-s-–debug看一看,是不是一切正常?然后测试是否能在新的电脑上发表文章。"><a href="#此时,你的博客就OK了,运行-hexo-s-–debug看一看,是不是一切正常?然后测试是否能在新的电脑上发表文章。" class="headerlink" title="此时,你的博客就OK了,运行 hexo s –debug看一看,是不是一切正常?然后测试是否能在新的电脑上发表文章。"></a>此时,你的博客就OK了,运行 hexo s –debug看一看,是不是一切正常?然后测试是否能在新的电脑上发表文章。</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo s --debug 然后打开`localhost:4000`就可以看见刚才发表的测试文章了</span><br></pre></td></tr></table></figure><h2 id="当在本地确认博客效果后,就可以将md文件生成静态网页上传至GithubPages,在终端定位到way1989-github-io目录下,执行下面的命令即可"><a href="#当在本地确认博客效果后,就可以将md文件生成静态网页上传至GithubPages,在终端定位到way1989-github-io目录下,执行下面的命令即可" class="headerlink" title="当在本地确认博客效果后,就可以将md文件生成静态网页上传至GithubPages,在终端定位到way1989.github.io目录下,执行下面的命令即可:"></a>当在本地确认博客效果后,就可以将md文件生成静态网页上传至GithubPages,在终端定位到way1989.github.io目录下,执行下面的命令即可:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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 clean <span class="comment">#清除网页缓存</span></span><br><span class="line">$ hexo g <span class="comment">#生成静态网页</span></span><br><span class="line">¥ hexo d <span class="comment">#开始部署</span></span><br><span class="line">//当然也可以使用一次性命令:</span><br><span class="line">¥ hexo clean &amp;&amp; hexo g &amp;&amp; hexo d</span><br></pre></td></tr></table></figure><h2 id="添加百度-谷歌-本地-自定义站点内容搜索"><a href="#添加百度-谷歌-本地-自定义站点内容搜索" class="headerlink" title="添加百度/谷歌/本地 自定义站点内容搜索"></a>添加百度/谷歌/本地 自定义站点内容搜索</h2><h3 id="安装hexo-generator-search,在站点的根目录下执行以下命令:"><a href="#安装hexo-generator-search,在站点的根目录下执行以下命令:" class="headerlink" title="安装hexo-generator-search,在站点的根目录下执行以下命令:"></a>安装<code>hexo-generator-search</code>,在站点的根目录下执行以下命令:</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-generator-search --save</span><br></pre></td></tr></table></figure><h3 id="编辑站点配置文件,新增以下内容到任意位置"><a href="#编辑站点配置文件,新增以下内容到任意位置" class="headerlink" title="编辑站点配置文件,新增以下内容到任意位置:"></a>编辑<code>站点配置文件</code>,新增以下内容到任意位置:</h3><figure class="highlight bash"><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">search:</span><br><span class="line"> path: search.xml</span><br><span class="line"> field: post</span><br></pre></td></tr></table></figure><h3 id="hexo部署失败-ERROR-Deployer-not-found-git"><a href="#hexo部署失败-ERROR-Deployer-not-found-git" class="headerlink" title="hexo部署失败 ERROR Deployer not found: git"></a>hexo部署失败 ERROR Deployer not found: git</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> Hexo </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>ubuntu新增SSD指南</title>
<link href="/2016/09/02/notes/AddSSDForUbuntu/"/>
<url>/2016/09/02/notes/AddSSDForUbuntu/</url>
<content type="html"><![CDATA[<h2 id="打开Applications–-gt-System-Tools–-gt-Preferences–-gt-Disk-Utility,先格式化240G的那个硬盘,选择ext4格式,然后创建分区:"><a href="#打开Applications–-gt-System-Tools–-gt-Preferences–-gt-Disk-Utility,先格式化240G的那个硬盘,选择ext4格式,然后创建分区:" class="headerlink" title="打开Applications–&gt;System Tools–&gt;Preferences–&gt;Disk Utility,先格式化240G的那个硬盘,选择ext4格式,然后创建分区:"></a>打开Applications–&gt;System Tools–&gt;Preferences–&gt;Disk Utility,先格式化240G的那个硬盘,选择ext4格式,然后创建分区:</h2><h2 id="查看硬盘信息,终端执行:-dev-sdb1可能不同,具体参考Disk-Utility中显示-:"><a href="#查看硬盘信息,终端执行:-dev-sdb1可能不同,具体参考Disk-Utility中显示-:" class="headerlink" title="查看硬盘信息,终端执行:(/dev/sdb1可能不同,具体参考Disk Utility中显示):"></a>查看硬盘信息,终端执行:(/dev/sdb1可能不同,具体参考Disk Utility中显示):</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo blkid /dev/sdb1</span><br></pre></td></tr></table></figure><h3 id="显示类似以下信息"><a href="#显示类似以下信息" class="headerlink" title="显示类似以下信息:"></a>显示类似以下信息:</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/dev/sdb1: LABEL=<span class="string">"SSD"</span> UUID=<span class="string">"861ada17-a2a2-4781-8ec7-5b93794adf9b"</span> TYPE=<span class="string">"ext4"</span></span><br></pre></td></tr></table></figure><h2 id="修改自动挂载点,在最后增加一行-需要先创建好SSD目录sudo-mkdir-SSD-:"><a href="#修改自动挂载点,在最后增加一行-需要先创建好SSD目录sudo-mkdir-SSD-:" class="headerlink" title="修改自动挂载点,在最后增加一行(需要先创建好SSD目录sudo mkdir /SSD):"></a>修改自动挂载点,在最后增加一行(需要先创建好SSD目录sudo mkdir /SSD):</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo vim /etc/fstab</span><br></pre></td></tr></table></figure><h3 id="显示类似以下信息-1"><a href="#显示类似以下信息-1" class="headerlink" title="显示类似以下信息:"></a>显示类似以下信息:</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">UUID=861ada17-a2a2-4781-8ec7-5b93794adf9b /SSD ext4 defaults 1 2</span><br></pre></td></tr></table></figure><h2 id="执行以下命令即可,以后开机都会自动挂载到-SSD目录了-目录名字可以随意取"><a href="#执行以下命令即可,以后开机都会自动挂载到-SSD目录了-目录名字可以随意取" class="headerlink" title="执行以下命令即可,以后开机都会自动挂载到/SSD目录了(目录名字可以随意取)"></a>执行以下命令即可,以后开机都会自动挂载到/SSD目录了(目录名字可以随意取)</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo mount -a</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> SSD </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>EventBus</title>
<link href="/2016/08/18/blogs/EventBus/"/>
<url>/2016/08/18/blogs/EventBus/</url>
<content type="html"><![CDATA[<h1 id="EventBus"><a href="#EventBus" class="headerlink" title="EventBus"></a>EventBus</h1><p>EventBus is a publish/subscribe event bus optimized for Android.<br><br><img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/eventbus/EventBus-Publish-Subscribe.png" width="500" height="187"></p><h2 id="EventBus介绍"><a href="#EventBus介绍" class="headerlink" title="EventBus介绍"></a>EventBus介绍</h2><ul><li>simplifies the communication between components<ul><li>decouples event senders and receivers </li><li>performs well with Activities, Fragments, and background threads</li><li>avoids complex and error-prone dependencies and life cycle issues</li></ul></li><li>makes your code simpler</li><li>is fast</li><li>is tiny (~50k jar)</li><li>is proven in practice by apps with 100,000,000+ installs</li><li>has advanced features like delivery threads, subscriber priorities, etc.</li></ul><h2 id="添加EventBus支持"><a href="#添加EventBus支持" class="headerlink" title="添加EventBus支持"></a>添加EventBus支持</h2><p>Gradle:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gradle</span><br><span class="line">compile &apos;org.greenrobot:eventbus:3.0.0&apos;</span><br></pre></td></tr></table></figure></p><p><a href="http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.greenrobot%22%20AND%20a%3A%22eventbus%22" target="_blank" rel="noopener">download EventBus from Maven Central</a></p><h2 id="EventBus-使用示例"><a href="#EventBus-使用示例" class="headerlink" title="EventBus 使用示例"></a>EventBus 使用示例</h2><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></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">LoadEvent</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="onCreate中注册事件"><a href="#onCreate中注册事件" class="headerlink" title="onCreate中注册事件"></a>onCreate中注册事件</h3><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">EventBus.getDefault().register(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure><h3 id="onDestroy中取消注册"><a href="#onDestroy中取消注册" class="headerlink" title="onDestroy中取消注册"></a>onDestroy中取消注册</h3><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">EventBus.getDefault().unregister(<span class="keyword">this</span>);</span><br></pre></td></tr></table></figure><h3 id="订阅事件"><a href="#订阅事件" class="headerlink" title="订阅事件"></a>订阅事件</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Subscribe</span>(threadMode = ThreadMode.MAIN, priority = <span class="number">0</span>, sticky = <span class="keyword">false</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onLoadEvent</span><span class="params">(LoadEvent event)</span> </span>&#123;</span><br><span class="line"> Log.d(TAG, <span class="string">"onLoadEvent"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>threadMode<ul><li>POSTING 在调用post所在的线程执行回调,直接运行。</li><li>MAIN 在主线程回调,如果post所在线程为主线程则直接执行,否则则通过mainThreadPoster来调度。</li><li>BACKGROUND 如果post所在线程为非UI线程则直接执行,否则则通过backgroundPoster来调度,这里只适合执行时间比较短的任务。</li><li>ASYNC(交给线程池来管理):直接通过asyncPoster调度。</li></ul></li><li>sticky 是否监听黏性事件 <blockquote><p>If true, delivers the most recent sticky event (posted with EventBus#postSticky(Object)) to this subscriber (if event available).</p></blockquote></li></ul><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></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().post(<span class="keyword">new</span> LoadEvent());</span><br></pre></td></tr></table></figure><h3 id="完整示例"><a href="#完整示例" class="headerlink" title="完整示例"></a>完整示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="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 class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"MainActivity"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line"> EventBus.getDefault().register(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line"> Button start = (Button) findViewById(R.id.start_load);</span><br><span class="line"> start.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>&#123;</span><br><span class="line"> EventBus.getDefault().post(<span class="keyword">new</span> LoadEvent());</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><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">super</span>.onDestroy();</span><br><span class="line"> EventBus.getDefault().unregister(<span class="keyword">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Subscribe</span>(threadMode = ThreadMode.MAIN, priority = <span class="number">0</span>, sticky = <span class="keyword">false</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onLoadEvent</span><span class="params">(LoadEvent event)</span> </span>&#123;</span><br><span class="line"> Log.d(TAG, <span class="string">"onLoadEvent"</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">LoadEvent</span> </span>&#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><h2 id="EventBus的注解生成索引"><a href="#EventBus的注解生成索引" class="headerlink" title="EventBus的注解生成索引"></a>EventBus的注解生成索引</h2><h3 id="项目的根目录build-gradle引入apt编译插件"><a href="#项目的根目录build-gradle引入apt编译插件" class="headerlink" title="项目的根目录build.gradle引入apt编译插件"></a>项目的根目录build.gradle引入apt编译插件</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">classpath</span> <span class="string">'com.neenbedankt.gradle.plugins:android-apt:1.8'</span></span><br></pre></td></tr></table></figure><h3 id="app的build-gradle-应用apt插件,并设置apt生成的索引的包名和类名"><a href="#app的build-gradle-应用apt插件,并设置apt生成的索引的包名和类名" class="headerlink" title="app的build.gradle 应用apt插件,并设置apt生成的索引的包名和类名"></a>app的build.gradle 应用apt插件,并设置apt生成的索引的包名和类名</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">apply plugin: <span class="string">'com.neenbedankt.android-apt'</span></span><br><span class="line">apt &#123;</span><br><span class="line"> arguments &#123;</span><br><span class="line"> eventBusIndex <span class="string">"com.android.gallery3d.MySubscriberInfoIndex"</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="app的dependencies中引入EventBusAnnotationProcessor:"><a href="#app的dependencies中引入EventBusAnnotationProcessor:" class="headerlink" title="app的dependencies中引入EventBusAnnotationProcessor:"></a>app的dependencies中引入EventBusAnnotationProcessor:</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt <span class="string">'org.greenrobot:eventbus-annotation-processor:3.0.1'</span></span><br></pre></td></tr></table></figure><h3 id="把注解生成的索引添加到EventBus默认的单例中,注意installDefaultEventBus这个方法只能调用一次"><a href="#把注解生成的索引添加到EventBus默认的单例中,注意installDefaultEventBus这个方法只能调用一次" class="headerlink" title="把注解生成的索引添加到EventBus默认的单例中,注意installDefaultEventBus这个方法只能调用一次"></a>把注解生成的索引添加到EventBus默认的单例中,注意installDefaultEventBus这个方法只能调用一次</h3><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">EventBus.builder().addIndex(<span class="keyword">new</span> MySubscriberInfoIndex()).installDefaultEventBus();</span><br></pre></td></tr></table></figure><h2 id="EventBus原理浅析"><a href="#EventBus原理浅析" class="headerlink" title="EventBus原理浅析"></a>EventBus原理浅析</h2><h3 id="EventBus-getDefault-方法"><a href="#EventBus-getDefault-方法" class="headerlink" title="EventBus.getDefault()方法"></a>EventBus.getDefault()方法</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> EventBus <span class="title">getDefault</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (defaultInstance == <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">synchronized</span> (EventBus.class) &#123;</span><br><span class="line"> <span class="keyword">if</span> (defaultInstance == <span class="keyword">null</span>) &#123;</span><br><span class="line"> defaultInstance = <span class="keyword">new</span> EventBus();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> defaultInstance;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="EventBus-getDefault-register-this-方法"><a href="#EventBus-getDefault-register-this-方法" class="headerlink" title="EventBus.getDefault().register(this)方法"></a>EventBus.getDefault().register(this)方法</h3><p> <img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/eventbus//register.png"></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">register</span><span class="params">(Object subscriber)</span> </span>&#123;</span><br><span class="line"> Class&lt;?&gt; subscriberClass = subscriber.getClass();</span><br><span class="line"> List&lt;SubscriberMethod&gt; subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line"> <span class="keyword">for</span> (SubscriberMethod subscriberMethod : subscriberMethods) &#123;</span><br><span class="line"> subscribe(subscriber, subscriberMethod);</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Map&lt;Class&lt;?&gt;, CopyOnWriteArrayList&lt;Subscription&gt;&gt; subscriptionsByEventType;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Map&lt;Object, List&lt;Class&lt;?&gt;&gt;&gt; typesBySubscriber; </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">subscribe</span><span class="params">(Object subscriber, SubscriberMethod subscriberMethod)</span> </span>&#123;</span><br><span class="line"> Class&lt;?&gt; eventType = subscriberMethod.eventType;</span><br><span class="line"> Subscription newSubscription = <span class="keyword">new</span> Subscription(subscriber, subscriberMethod);</span><br><span class="line"> CopyOnWriteArrayList&lt;Subscription&gt; subscriptions = subscriptionsByEventType.get(eventType);</span><br><span class="line"> <span class="keyword">if</span> (subscriptions == <span class="keyword">null</span>) &#123;</span><br><span class="line"> subscriptions = <span class="keyword">new</span> CopyOnWriteArrayList&lt;&gt;();</span><br><span class="line"> subscriptionsByEventType.put(eventType, subscriptions);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (subscriptions.contains(newSubscription)) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> EventBusException(<span class="string">"Subscriber "</span> + subscriber.getClass() + <span class="string">" already registered to event "</span></span><br><span class="line"> + eventType);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> size = subscriptions.size();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= size; i++) &#123;</span><br><span class="line"> <span class="keyword">if</span> (i == size || subscriberMethod.priority &gt; subscriptions.get(i).subscriberMethod.priority) &#123;</span><br><span class="line"> subscriptions.add(i, newSubscription);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> List&lt;Class&lt;?&gt;&gt; subscribedEvents = typesBySubscriber.get(subscriber);</span><br><span class="line"> <span class="keyword">if</span> (subscribedEvents == <span class="keyword">null</span>) &#123;</span><br><span class="line"> subscribedEvents = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"> typesBySubscriber.put(subscriber, subscribedEvents);</span><br><span class="line"> &#125;</span><br><span class="line"> subscribedEvents.add(eventType);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (subscriberMethod.sticky) &#123;</span><br><span class="line"> <span class="keyword">if</span> (eventInheritance) &#123;</span><br><span class="line"> <span class="comment">// Existing sticky events of all subclasses of eventType have to be considered.</span></span><br><span class="line"> <span class="comment">// Note: Iterating over all events may be inefficient with lots of sticky events,</span></span><br><span class="line"> <span class="comment">// thus data structure should be changed to allow a more efficient lookup</span></span><br><span class="line"> <span class="comment">// (e.g. an additional map storing sub classes of super classes: Class -&gt; List&lt;Class&gt;).</span></span><br><span class="line"> Set&lt;Map.Entry&lt;Class&lt;?&gt;, Object&gt;&gt; entries = stickyEvents.entrySet();</span><br><span class="line"> <span class="keyword">for</span> (Map.Entry&lt;Class&lt;?&gt;, Object&gt; entry : entries) &#123;</span><br><span class="line"> Class&lt;?&gt; candidateEventType = entry.getKey();</span><br><span class="line"> <span class="keyword">if</span> (eventType.isAssignableFrom(candidateEventType)) &#123;</span><br><span class="line"> Object stickyEvent = entry.getValue();</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> Object stickyEvent = stickyEvents.get(eventType);</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</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></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">checkPostStickyEventToSubscription</span><span class="params">(Subscription newSubscription, Object stickyEvent)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (stickyEvent != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="comment">// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)</span></span><br><span class="line"> <span class="comment">// --&gt; Strange corner case, which we don't take care of here.</span></span><br><span class="line"> postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">postToSubscription</span><span class="params">(Subscription subscription, Object event, <span class="keyword">boolean</span> isMainThread)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">switch</span> (subscription.subscriberMethod.threadMode) &#123;</span><br><span class="line"> <span class="keyword">case</span> POSTING:</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> MAIN:</span><br><span class="line"> <span class="keyword">if</span> (isMainThread) &#123;</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> mainThreadPoster.enqueue(subscription, event);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> BACKGROUND:</span><br><span class="line"> <span class="keyword">if</span> (isMainThread) &#123;</span><br><span class="line"> backgroundPoster.enqueue(subscription, event);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ASYNC:</span><br><span class="line"> asyncPoster.enqueue(subscription, event);</span><br><span class="line"> <span class="keyword">break</span>;</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> IllegalStateException(<span class="string">"Unknown thread mode: "</span> + subscription.subscriberMethod.threadMode);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="EventBus-getDefault-post-new-LoadEvent"><a href="#EventBus-getDefault-post-new-LoadEvent" class="headerlink" title="EventBus.getDefault().post(new LoadEvent());"></a>EventBus.getDefault().post(new LoadEvent());</h3><p> <img src="https://raw.githubusercontent.com/way1989/way1989.github.io/hexo/images_post/eventbus//post.png"></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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">post</span><span class="params">(Object event)</span> </span>&#123;</span><br><span class="line"> PostingThreadState postingState = currentPostingThreadState.get();</span><br><span class="line"> List&lt;Object&gt; eventQueue = postingState.eventQueue;</span><br><span class="line"> eventQueue.add(event);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!postingState.isPosting) &#123;</span><br><span class="line"> postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();</span><br><span class="line"> postingState.isPosting = <span class="keyword">true</span>;</span><br><span class="line"> <span class="keyword">if</span> (postingState.canceled) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> EventBusException(<span class="string">"Internal error. Abort state was not reset"</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="keyword">while</span> (!eventQueue.isEmpty()) &#123;</span><br><span class="line"> postSingleEvent(eventQueue.remove(<span class="number">0</span>), postingState);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"> postingState.isPosting = <span class="keyword">false</span>;</span><br><span class="line"> postingState.isMainThread = <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></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><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">void</span> <span class="title">postSingleEvent</span><span class="params">(Object event, PostingThreadState postingState)</span> <span class="keyword">throws</span> Error </span>&#123;</span><br><span class="line"> Class&lt;?&gt; eventClass = event.getClass();</span><br><span class="line"> <span class="keyword">boolean</span> subscriptionFound = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">if</span> (eventInheritance) &#123;</span><br><span class="line"> List&lt;Class&lt;?&gt;&gt; eventTypes = lookupAllEventTypes(eventClass);</span><br><span class="line"> <span class="keyword">int</span> countTypes = eventTypes.size();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> h = <span class="number">0</span>; h &lt; countTypes; h++) &#123;</span><br><span class="line"> Class&lt;?&gt; clazz = eventTypes.get(h);</span><br><span class="line"> subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (!subscriptionFound) &#123;</span><br><span class="line"> <span class="keyword">if</span> (logNoSubscriberMessages) &#123;</span><br><span class="line"> Log.d(TAG, <span class="string">"No subscribers registered for event "</span> + eventClass);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (sendNoSubscriberEvent &amp;&amp; eventClass != NoSubscriberEvent.class &amp;&amp;</span><br><span class="line"> eventClass != SubscriberExceptionEvent.class) &#123;</span><br><span class="line"> post(<span class="keyword">new</span> NoSubscriberEvent(<span class="keyword">this</span>, event));</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">postSingleEventForEventType</span><span class="params">(Object event, PostingThreadState postingState, Class&lt;?&gt; eventClass)</span> </span>&#123;</span><br><span class="line"> CopyOnWriteArrayList&lt;Subscription&gt; subscriptions;</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line"> subscriptions = subscriptionsByEventType.get(eventClass);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (subscriptions != <span class="keyword">null</span> &amp;&amp; !subscriptions.isEmpty()) &#123;</span><br><span class="line"> <span class="keyword">for</span> (Subscription subscription : subscriptions) &#123;</span><br><span class="line"> postingState.event = event;</span><br><span class="line"> postingState.subscription = subscription;</span><br><span class="line"> <span class="keyword">boolean</span> aborted = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> postToSubscription(subscription, event, postingState.isMainThread);</span><br><span class="line"> aborted = postingState.canceled;</span><br><span class="line"> &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"> postingState.event = <span class="keyword">null</span>;</span><br><span class="line"> postingState.subscription = <span class="keyword">null</span>;</span><br><span class="line"> postingState.canceled = <span class="keyword">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (aborted) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">postToSubscription</span><span class="params">(Subscription subscription, Object event, <span class="keyword">boolean</span> isMainThread)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">switch</span> (subscription.subscriberMethod.threadMode) &#123;</span><br><span class="line"> <span class="keyword">case</span> POSTING:</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> MAIN:</span><br><span class="line"> <span class="keyword">if</span> (isMainThread) &#123;</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> mainThreadPoster.enqueue(subscription, event);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> BACKGROUND:</span><br><span class="line"> <span class="keyword">if</span> (isMainThread) &#123;</span><br><span class="line"> backgroundPoster.enqueue(subscription, event);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ASYNC:</span><br><span class="line"> asyncPoster.enqueue(subscription, event);</span><br><span class="line"> <span class="keyword">break</span>;</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> IllegalStateException(<span class="string">"Unknown thread mode: "</span> + subscription.subscriberMethod.threadMode);</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">invokeSubscriber</span><span class="params">(Subscription subscription, Object event)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> subscription.subscriberMethod.method.invoke(subscription.subscriber, event);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line"> handleSubscriberException(subscription, event, e.getCause());</span><br><span class="line"> &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Unexpected exception"</span>, e);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="EventBus-getDefault-unregister-this"><a href="#EventBus-getDefault-unregister-this" class="headerlink" title="EventBus.getDefault().unregister(this);"></a>EventBus.getDefault().unregister(this);</h3><p> <img src="https://raw.githubusercontent.com/way1989/way1989.github.io/master/images/unregister.png"></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="keyword">private</span> <span class="keyword">final</span> Map&lt;Object, List&lt;Class&lt;?&gt;&gt;&gt; typesBySubscriber;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">unregister</span><span class="params">(Object subscriber)</span> </span>&#123;</span><br><span class="line"> List&lt;Class&lt;?&gt;&gt; subscribedTypes = typesBySubscriber.get(subscriber);</span><br><span class="line"> <span class="keyword">if</span> (subscribedTypes != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">for</span> (Class&lt;?&gt; eventType : subscribedTypes) &#123;</span><br><span class="line"> unsubscribeByEventType(subscriber, eventType);</span><br><span class="line"> &#125;</span><br><span class="line"> typesBySubscriber.remove(subscriber);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> Log.w(TAG, <span class="string">"Subscriber to unregister was not registered before: "</span> + subscriber.getClass());</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Map&lt;Class&lt;?&gt;, CopyOnWriteArrayList&lt;Subscription&gt;&gt; subscriptionsByEventType;</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">unsubscribeByEventType</span><span class="params">(Object subscriber, Class&lt;?&gt; eventType)</span> </span>&#123;</span><br><span class="line"> List&lt;Subscription&gt; subscriptions = subscriptionsByEventType.get(eventType);</span><br><span class="line"> <span class="keyword">if</span> (subscriptions != <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">int</span> size = subscriptions.size();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line"> Subscription subscription = subscriptions.get(i);</span><br><span class="line"> <span class="keyword">if</span> (subscription.subscriber == subscriber) &#123;</span><br><span class="line"> subscription.active = <span class="keyword">false</span>;</span><br><span class="line"> subscriptions.remove(i);</span><br><span class="line"> i--;</span><br><span class="line"> size--;</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><h2 id="EventBus缺点"><a href="#EventBus缺点" class="headerlink" title="EventBus缺点"></a>EventBus缺点</h2><ul><li>耦合性太低</li><li>订阅的事件是利用反射的机制执行的</li><li>不能跨进程</li></ul><h2 id="用RxJava实现简易RxBus"><a href="#用RxJava实现简易RxBus" class="headerlink" title="用RxJava实现简易RxBus"></a>用RxJava实现简易RxBus</h2><h3 id="实现RxBus"><a href="#实现RxBus" class="headerlink" title="实现RxBus"></a>实现RxBus</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></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">RxBus</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> RxBus sInstance;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Subject subject;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="title">RxBus</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> subject = <span class="keyword">new</span> SerializedSubject&lt;&gt;(PublishSubject.create());</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> RxBus <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line"> <span class="keyword">if</span> (sInstance == <span class="keyword">null</span>) &#123;</span><br><span class="line"> <span class="keyword">synchronized</span> (RxBus.class) &#123;</span><br><span class="line"> <span class="keyword">if</span> (sInstance == <span class="keyword">null</span>) &#123;</span><br><span class="line"> sInstance = <span class="keyword">new</span> RxBus();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sInstance;</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">post</span><span class="params">(Object event)</span> </span>&#123;</span><br><span class="line"> subject.onNext(event);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> &lt;T&gt; <span class="function">Observable&lt;T&gt; <span class="title">toObservable</span><span class="params">(Class&lt;T&gt; eventType)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> subject.ofType(eventType);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="订阅事件-1"><a href="#订阅事件-1" 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></pre></td><td class="code"><pre><span class="line">RxBus.getInstance().toObservable(ForceUpdateEvent.class)</span><br><span class="line"> .observeOn(AndroidSchedulers.mainThread())</span><br><span class="line"> .compose(<span class="keyword">this</span>.&lt;ForceUpdateEvent&gt;bindUntilEvent(ActivityEvent.DESTROY))</span><br><span class="line"> .subscribe(<span class="keyword">new</span> Action1&lt;ForceUpdateEvent&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">(ForceUpdateEvent forceUpdateEvent)</span> </span>&#123;</span><br><span class="line"> <span class="comment">//do some thing</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br></pre></td></tr></table></figure><h3 id="发布事件-1"><a href="#发布事件-1" class="headerlink" title="发布事件"></a>发布事件</h3><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">RxBus.getInstance().post(<span class="keyword">new</span> ForceUpdateEvent(parameter));</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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ForceUpdateEvent</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul><li><a href="https://github.com/greenrobot/EventBus" target="_blank" rel="noopener">EventBus github</a></li><li><a href="http://www.cnblogs.com/bugly/p/5475034.html" target="_blank" rel="noopener">Bugly干货分享】老司机教你 “飙” EventBus 3</a></li><li><a href="http://blog.csdn.net/lmj623565791/article/details/40920453" target="_blank" rel="noopener">Android EventBus源码解析 带你深入理解EventBus</a></li><li><a href="http://www.jianshu.com/p/ca090f6e2fe2" target="_blank" rel="noopener">用RxJava实现事件总线(Event Bus)</a></li></ul>]]></content>
<categories>
<category> 博客 </category>
<category> EventBus </category>
</categories>
<tags>
<tag> 博客 </tag>
</tags>
</entry>
<entry>
<title>将.pem与.pk8文件转换成.keystore签名文件</title>
<link href="/2016/08/04/notes/ApkKeystore/"/>
<url>/2016/08/04/notes/ApkKeystore/</url>
<content type="html"><![CDATA[<h2 id="新建一个platform目录,将平台用到的两个文件platform-x509-pem和platform-pk8拷贝过来,通常在build-target-product-security-目录下-普通签名方式是:"><a href="#新建一个platform目录,将平台用到的两个文件platform-x509-pem和platform-pk8拷贝过来,通常在build-target-product-security-目录下-普通签名方式是:" class="headerlink" title="新建一个platform目录,将平台用到的两个文件platform.x509.pem和platform.pk8拷贝过来,通常在build/target/product/security/目录下,普通签名方式是:"></a>新建一个platform目录,将平台用到的两个文件platform.x509.pem和platform.pk8拷贝过来,通常在build/target/product/security/目录下,普通签名方式是:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line">inPath=<span class="variable">$1</span></span><br><span class="line">outPath=<span class="variable">$2</span></span><br><span class="line"></span><br><span class="line">java -jar out/host/linux-x86/framework/signapk.jar build/target/product/security/tinno_common/platform.x509.pem build/target/product/security/tinno_common/platform.pk8 <span class="variable">$&#123;inPath&#125;</span> <span class="variable">$&#123;outPath&#125;</span></span><br></pre></td></tr></table></figure><h2 id="把pkcs8格式的私钥转换为pkcs12格式,生成platform-priv-pem文件:"><a href="#把pkcs8格式的私钥转换为pkcs12格式,生成platform-priv-pem文件:" class="headerlink" title="把pkcs8格式的私钥转换为pkcs12格式,生成platform.priv.pem文件:"></a>把pkcs8格式的私钥转换为pkcs12格式,生成platform.priv.pem文件:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ openssl pkcs8 -<span class="keyword">in</span> platform.pk8 -inform DER -outform PEM -out platform.priv.pem -nocrypt</span><br></pre></td></tr></table></figure><h2 id="生成pkcs12格式的密钥文件-生成platform-pk12文件,最后的brilliance是keystore的alias,需要输入两次密码,我们这里默认为android。"><a href="#生成pkcs12格式的密钥文件-生成platform-pk12文件,最后的brilliance是keystore的alias,需要输入两次密码,我们这里默认为android。" class="headerlink" title="生成pkcs12格式的密钥文件,生成platform.pk12文件,最后的brilliance是keystore的alias,需要输入两次密码,我们这里默认为android。"></a>生成pkcs12格式的密钥文件,生成platform.pk12文件,最后的brilliance是keystore的alias,需要输入两次密码,我们这里默认为android。</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ openssl pkcs12 -<span class="built_in">export</span> -<span class="keyword">in</span> platform.x509.pem -inkey platform.priv.pem -out platform.pk12 -name brilliance</span><br></pre></td></tr></table></figure><h2 id="生成platform-keystore"><a href="#生成platform-keystore" class="headerlink" title="生成platform.keystore"></a>生成platform.keystore</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ keytool -importkeystore -deststorepass android -destkeypass android -destkeystore platform.keystore -srckeystore platform.pk12 -srcstoretype PKCS12 -srcstorepass android -<span class="built_in">alias</span> brilliance</span><br></pre></td></tr></table></figure><h2 id="使用-keystore签名的脚本signapk:"><a href="#使用-keystore签名的脚本signapk:" class="headerlink" title="使用.keystore签名的脚本signapk:"></a>使用.keystore签名的脚本signapk:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment"># Sample usage is as follows;</span></span><br><span class="line"><span class="comment"># ./signapk myapp.apk debug.keystore android androiddebugkey</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># param1, APK file: Calculator_debug.apk</span></span><br><span class="line"><span class="comment"># param2, keystore location: ~/.android/debug.keystore</span></span><br><span class="line"><span class="comment"># param3, key storepass: android</span></span><br><span class="line"><span class="comment"># param4, key alias: androiddebugkey</span></span><br><span class="line"></span><br><span class="line">USER_HOME=$(<span class="built_in">eval</span> <span class="built_in">echo</span> ~<span class="variable">$&#123;SUDO_USER&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># use my debug key default</span></span><br><span class="line">APK=<span class="variable">$1</span></span><br><span class="line">KEYSTORE=<span class="string">"<span class="variable">$&#123;2:-$USER_HOME/.android/debug.keystore&#125;</span>"</span></span><br><span class="line">STOREPASS=<span class="string">"<span class="variable">$&#123;3:-android&#125;</span>"</span></span><br><span class="line">ALIAS=<span class="string">"<span class="variable">$&#123;4:-androiddebugkey&#125;</span>"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># get the filename</span></span><br><span class="line">APK_BASENAME=$(basename <span class="variable">$APK</span>)</span><br><span class="line">SIGNED_APK=<span class="string">"signed_"</span><span class="variable">$APK_BASENAME</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#debug</span></span><br><span class="line"><span class="built_in">echo</span> param1 <span class="variable">$APK</span></span><br><span class="line"><span class="built_in">echo</span> param2 <span class="variable">$KEYSTORE</span></span><br><span class="line"><span class="built_in">echo</span> param3 <span class="variable">$STOREPASS</span></span><br><span class="line"><span class="built_in">echo</span> param4 <span class="variable">$ALIAS</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># delete META-INF folder</span></span><br><span class="line">zip -d <span class="variable">$APK</span> META-INF/\*</span><br><span class="line"></span><br><span class="line"><span class="comment"># sign APK</span></span><br><span class="line">jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore <span class="variable">$KEYSTORE</span> -storepass <span class="variable">$STOREPASS</span> <span class="variable">$APK</span> <span class="variable">$ALIAS</span></span><br><span class="line"><span class="comment">#verify</span></span><br><span class="line">jarsigner -verify <span class="variable">$APK</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#zipalign</span></span><br><span class="line">zipalign -v 4 <span class="variable">$APK</span> <span class="variable">$SIGNED_APK</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> keystore </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>下载动画,图标飞入下载管理动画实现</title>
<link href="/2016/07/02/notes/AndroidAnimation/"/>
<url>/2016/07/02/notes/AndroidAnimation/</url>
<content type="html"><![CDATA[<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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Button sActionBarDownload; <span class="comment">//title栏的下载 </span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> FrameLayout sParentView; <span class="comment">//获取父view </span></span><br><span class="line"> <span class="keyword">protected</span> ImageView mImageSwitcher; </span><br><span class="line"> <span class="comment">// 下载飞出动画的视图 </span></span><br><span class="line"> <span class="keyword">public</span> ImageView mAnimationImageView = <span class="keyword">null</span>; </span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取屏幕的整个大view </span></span><br><span class="line"> sParentView = (FrameLayout) findViewById(R.id.parent_view); </span><br><span class="line"> mActionBarContainer = (ActionBarContainer) findViewById(R.id.actionbar); </span><br><span class="line"> sActionBarDownload = (Button) findViewById(R.id.btn_download_recommend); </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">iconFlyOutAnimation</span><span class="params">(<span class="keyword">final</span> AppInfoDataBean app, <span class="keyword">final</span> View v)</span> </span>&#123; </span><br><span class="line"> <span class="keyword">int</span>[] location = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>]; </span><br><span class="line"> mImageSwitcher.getLocationInWindow(location); <span class="comment">//要下载应用图标的位置,相对于window的位置 </span></span><br><span class="line"> <span class="keyword">int</span> width = mImageSwitcher.getWidth(); </span><br><span class="line"> <span class="keyword">int</span> height = mImageSwitcher.getHeight(); </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (MyActivity.sParentView != <span class="keyword">null</span>) <span class="comment">//title下载的位置 </span></span><br><span class="line"> &#123; </span><br><span class="line"> <span class="comment">//获取到目标view相对于window的位置 </span></span><br><span class="line"> <span class="keyword">int</span>[] locationRelative = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>]; </span><br><span class="line"> MyActivity.sActionBarDownload.getLocationInWindow(locationRelative); </span><br><span class="line"></span><br><span class="line"> <span class="comment">//将源view放到ImageView中 </span></span><br><span class="line"> mAnimationImageView = <span class="keyword">new</span> ImageView(getContext()); </span><br><span class="line"> FrameLayout.LayoutParams params = <span class="keyword">new</span> FrameLayout.LayoutParams(width, height); </span><br><span class="line"> params.leftMargin = locationRelative[<span class="number">0</span>] - location[<span class="number">0</span>]; </span><br><span class="line"> params.topMargin = location[<span class="number">1</span>] - locationRelative[<span class="number">1</span>]; </span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取到源图片的当前view,并设置到Drawable </span></span><br><span class="line"> ImageView iconImage = (ImageView) mImageSwitcher.getCurrentView(); </span><br><span class="line"> mAnimationImageView.setImageDrawable(iconImage.getDrawable()); </span><br><span class="line"> <span class="comment">//将动画添加到控件内,源加到目标中 </span></span><br><span class="line"> MyActivity.sParentView.addView(mAnimationImageView, params); </span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取目标的位置控件的长、宽 </span></span><br><span class="line"> <span class="keyword">int</span>[] locationDst = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>]; </span><br><span class="line"> locationDst[<span class="number">0</span>] = MyActivity.sActionBarDownload.getWidth(); </span><br><span class="line"> locationDst[<span class="number">1</span>] = MyActivity.sActionBarDownload.getHeight(); </span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> destXp = locationDst[<span class="number">0</span>] / <span class="number">2</span>; </span><br><span class="line"> <span class="keyword">int</span> destYp = locationDst[<span class="number">1</span>] / <span class="number">2</span>; </span><br><span class="line"> <span class="keyword">int</span> destX = params.leftMargin - destXp; </span><br><span class="line"> <span class="keyword">int</span> destY = params.topMargin - destYp + DrawUtils.dip2px(<span class="number">20</span>); </span><br><span class="line"> <span class="comment">//位移 </span></span><br><span class="line"> Animation translateAnimation = <span class="keyword">new</span> TranslateAnimation(-destX, DrawUtils.dip2px(<span class="number">12</span>), <span class="number">0</span>, -destY); </span><br><span class="line"> translateAnimation.setDuration(<span class="number">600</span>); </span><br><span class="line"> <span class="comment">//缩放 </span></span><br><span class="line"> Animation scaleAnimation = <span class="keyword">new</span> ScaleAnimation(<span class="number">1</span>, <span class="number">0.1f</span>, <span class="number">1</span>, <span class="number">0.1f</span>, Animation.RELATIVE_TO_SELF, <span class="number">0.5f</span>, </span><br><span class="line"> Animation.RELATIVE_TO_SELF, <span class="number">0.5f</span>); </span><br><span class="line"> scaleAnimation.setDuration(<span class="number">600</span>); </span><br><span class="line"> <span class="comment">//旋转动画效果 </span></span><br><span class="line"> Animation rotateAnimation = <span class="keyword">new</span> RotateAnimation(<span class="number">0f</span>, <span class="number">720</span>, Animation.RELATIVE_TO_SELF, <span class="number">0.5f</span>, </span><br><span class="line"> Animation.RELATIVE_TO_SELF, <span class="number">0.5f</span>); </span><br><span class="line"> rotateAnimation.setDuration(<span class="number">600</span>); </span><br><span class="line"></span><br><span class="line"> AnimationSet animationSet = <span class="keyword">new</span> AnimationSet(<span class="keyword">true</span>); </span><br><span class="line"> animationSet.setInterpolator(<span class="keyword">new</span> DecelerateInterpolator(<span class="number">2.0f</span>)); </span><br><span class="line"></span><br><span class="line"> animationSet.addAnimation(rotateAnimation); </span><br><span class="line"> animationSet.addAnimation(scaleAnimation); </span><br><span class="line"> animationSet.addAnimation(translateAnimation); </span><br><span class="line"> <span class="comment">// BaseDataActivity.mActionBarDownload.InterceptTouchEvent(true); //设置不可点击 </span></span><br><span class="line"></span><br><span class="line"> animationSet.setAnimationListener(<span class="keyword">new</span> AnimationListener() &#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">onAnimationEnd</span><span class="params">(Animation animation)</span> </span>&#123; </span><br><span class="line"> <span class="keyword">if</span> (mAnimationImageView != <span class="keyword">null</span> &amp;&amp; mAnimationImageView.getTag() == animation) &#123; </span><br><span class="line"> MyActivity.sParentView.removeView(mAnimationImageView); </span><br><span class="line"></span><br><span class="line"> mAnimationImageView = <span class="keyword">null</span>; </span><br><span class="line"> <span class="comment">// 运行完动画再下载 </span></span><br><span class="line"> downLoad(app); </span><br><span class="line"> &#125; </span><br><span class="line"> <span class="comment">// BaseDataActivity.mActionBarDownload.onInterceptTouchEvent(false); </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">onAnimationRepeat</span><span class="params">(Animation animation)</span> </span>&#123; </span><br><span class="line"></span><br><span class="line"> &#125; </span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAnimationStart</span><span class="params">(Animation animation)</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"> mAnimationImageView.startAnimation(animationSet); </span><br><span class="line"> mAnimationImageView.setTag(animationSet); </span><br><span class="line"> &#125; </span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> Animation </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>蓝牙传输文件代码</title>
<link href="/2016/06/05/notes/BluetoothShare/"/>
<url>/2016/06/05/notes/BluetoothShare/</url>
<content type="html"><![CDATA[<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><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></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">sendFile</span><span class="params">(Activity activity)</span> </span>&#123;</span><br><span class="line"> PackageManager localPackageManager = activity.getPackageManager();</span><br><span class="line"> Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line"> HashMap&lt;String, ActivityInfo&gt; shareItems = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> intent.setAction(Intent.ACTION_SEND);</span><br><span class="line"> String filepath = App.getContext().getPackageManager()</span><br><span class="line"> .getApplicationInfo(BuildConfig.APPLICATION_ID, <span class="number">0</span>).sourceDir;</span><br><span class="line"> File file = <span class="keyword">new</span> File(filepath);</span><br><span class="line"> intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));</span><br><span class="line"> <span class="comment">// intent.putExtra(Intent.EXTRA_STREAM,</span></span><br><span class="line"> <span class="comment">// Uri.fromFile(new File(localApplicationInfo.sourceDir)));</span></span><br><span class="line"> intent.setType(<span class="string">"*/*"</span>);</span><br><span class="line"> List&lt;ResolveInfo&gt; resolveInfos = localPackageManager.queryIntentActivities(intent, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">for</span>(ResolveInfo resolveInfo : resolveInfos)&#123;</span><br><span class="line"> ActivityInfo activityInfo = resolveInfo.activityInfo;</span><br><span class="line"> String progressName = activityInfo.applicationInfo.processName;</span><br><span class="line"> <span class="keyword">if</span>(progressName.contains(<span class="string">"bluetooth"</span>))</span><br><span class="line"> shareItems.put(progressName, activityInfo);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> Log.e(e.getMessage(), e);</span><br><span class="line"> &#125;</span><br><span class="line"> ActivityInfo activityInfo = shareItems.get(<span class="string">"com.android.bluetooth"</span>);</span><br><span class="line"> <span class="keyword">if</span> (activityInfo == <span class="keyword">null</span>)</span><br><span class="line"> activityInfo = shareItems.get(<span class="string">"com.mediatek.bluetooth"</span>);</span><br><span class="line"> <span class="keyword">if</span> (activityInfo == <span class="keyword">null</span>) &#123;</span><br><span class="line"> Iterator&lt;ActivityInfo&gt; iterator = shareItems.values().iterator();</span><br><span class="line"> <span class="keyword">if</span> (iterator.hasNext())</span><br><span class="line"> activityInfo = iterator.next();</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (activityInfo != <span class="keyword">null</span>) &#123;</span><br><span class="line"> intent.setComponent(<span class="keyword">new</span> ComponentName(activityInfo.packageName, activityInfo.name));</span><br><span class="line"> activity.startActivityForResult(intent, BLUETOOTH_SHARE_REQUEST_CODE);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 笔记 </category>
<category> Bluetooth </category>
</categories>
<tags>
<tag> 笔记 </tag>
</tags>
</entry>
<entry>
<title>Capturer帮助文档</title>
<link href="/2016/05/15/help/CapturerHelp/"/>
<url>/2016/05/15/help/CapturerHelp/</url>
<content type="html"><![CDATA[<h1 id="Capturer使用说明"><a href="#Capturer使用说明" class="headerlink" title="Capturer使用说明"></a>Capturer使用说明</h1><p><a href="http://fir.im/capturer" target="_blank" rel="noopener">Capturer</a>是为Android平台设计的,集<code>截屏、长截屏、录屏以及将录屏内容转换成GIF动态图</code>等多功能于一体的,小巧强悍的实用型工具软件。</p><h2 id="普通截屏"><a href="#普通截屏" class="headerlink" title="普通截屏"></a>普通截屏</h2><h3 id="操作方式"><a href="#操作方式" class="headerlink" title="操作方式"></a>操作方式</h3><p> 确保<strong>Capturer</strong>已经正常启动,进入到任何需要截屏的应用,摇一摇启动Captain主菜单,点击顺时针<code>两点钟位置的图标</code>开始截屏(长按会有提示)。</p><p> 与系统<code>长按电源键+音量键</code>截屏是一样的。</p><p> 普通截屏演示范例:</p><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/normal_screenshot.gif" alt="Long Screenshot"></p><h2 id="录屏"><a href="#录屏" class="headerlink" title="录屏"></a>录屏</h2><h3 id="操作方式-1"><a href="#操作方式-1" class="headerlink" title="操作方式"></a>操作方式</h3><p> 确保<strong>Capturer</strong>已经正常启动,进入到任何需要录屏的应用,摇一摇启动Captain主菜单,点击顺时针<code>四点钟位置的图标</code>开始录屏(长按会有提示)。</p><p> 当屏幕上方录屏操作菜单后,还没真正开始录屏,可以点击<code>左边的按钮</code>取消录屏,点击<code>右边的按钮</code>切换录制视频的画质质量(全高清画质与屏幕分辨率一致,高清为屏幕分辨率的75%,标清为50%),点击<code>中间的按钮</code>才开始真正的录屏。</p><p> 倒计时3秒后,显示停止按钮并计时表示真正开始录屏了,可以通过点击<code>停止按钮</code>随时结束录屏。</p><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><p> <strong>画质越高,录制的视频文件就越大,显示效果越好</strong>。<br> <strong>3秒倒计时可以在设置中取消</strong>。<br> <strong>停止按钮可以设置显示在通知栏中</strong>。</p><h3 id="录屏演示"><a href="#录屏演示" class="headerlink" title="录屏演示"></a>录屏演示</h3><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/screen_record.gif" alt="Long Screenshot"></p><h2 id="长截屏"><a href="#长截屏" class="headerlink" title="长截屏"></a>长截屏</h2><h3 id="操作方式-2"><a href="#操作方式-2" class="headerlink" title="操作方式"></a>操作方式</h3><p> 确保<strong>Capturer</strong>已经正常启动,进入到需要长截屏的应用,摇一摇启动Captain主菜单,点击顺时针<code>六点钟位置的图标</code>开始长截屏(长按会有提示)。</p><p> 当屏幕上方显示提示界面后,按照屏幕箭头的方向滚动与<strong>箭头长度相同</strong>的高度。</p><p> 滚动完一次之后,点击一次上方的提示界面,再接着滚动一次,点击一次,如果想结束长截屏,直接点击两次即可。</p><h3 id="注意事项-1"><a href="#注意事项-1" class="headerlink" title="注意事项"></a>注意事项</h3><p> <strong>滚动一次,点击一次,连点两次直接结束</strong>。<br> <strong>每次滚动的高度尽量与箭头长度相近,避免滚动过大导致长图拼接失败</strong>。</p><h3 id="长截屏演示"><a href="#长截屏演示" class="headerlink" title="长截屏演示"></a>长截屏演示</h3><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/long_screenshot.gif" alt="Long Screenshot"></p><h2 id="任意截屏"><a href="#任意截屏" class="headerlink" title="任意截屏"></a>任意截屏</h2><h3 id="操作方式-3"><a href="#操作方式-3" class="headerlink" title="操作方式"></a>操作方式</h3><p> 确保<strong>Capturer</strong>已经正常启动,进入到需要任意截屏的应用,摇一摇启动Captain主菜单,点击顺时针<code>十点钟位置的图标</code>开始任意截屏(长按会有提示)。</p><p> 当屏幕中显示提示界面,按照提示用手指圈选需要截取的部分,点击<code>确定</code>按钮保存截图。</p><p> 如果需要重新选取截屏区域,直接用手指圈选新的区域即可。</p><h3 id="注意事项-2"><a href="#注意事项-2" class="headerlink" title="注意事项"></a>注意事项</h3><p> <strong>保存圈选区域内容需要点击<code>确定</code>按钮</strong>。<br> <strong>选择新区域时,不需要做其他操作,直接在屏幕上重新圈选</strong>。<br> <strong>不圈选直接点击<code>确定</code>按钮,将直接保存完整屏幕截图</strong>。</p><h3 id="任意截屏演示"><a href="#任意截屏演示" class="headerlink" title="任意截屏演示"></a>任意截屏演示</h3><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/free_screenshot.gif" alt="Long Screenshot"></p><h2 id="矩形区域截屏"><a href="#矩形区域截屏" class="headerlink" title="矩形区域截屏"></a>矩形区域截屏</h2><h3 id="操作方式-4"><a href="#操作方式-4" class="headerlink" title="操作方式"></a>操作方式</h3><p> 确保<strong>Capturer</strong>已经正常启动,进入到需要矩形区域截屏的应用,摇一摇启动Captain主菜单,点击顺时针<code>十二点种位置的图标</code>开始矩形区域截屏(长按会有提示)。</p><p> 当屏幕中显示提示界面,按照提示,调整矩形框的大小和拖动矩形框的位置来选择需要截屏的部分。</p><p> 双击屏幕即可保存所选区域截屏内容。</p><h3 id="注意事项-3"><a href="#注意事项-3" class="headerlink" title="注意事项"></a>注意事项</h3><p> <strong>通过矩形框四个角调整大小,通过触摸矩形框其他区域可以拖动</strong>。<br> <strong>双击即可保存矩形框区域内的截屏内容</strong>。</p><h3 id="矩形区域截屏演示"><a href="#矩形区域截屏演示" class="headerlink" title="矩形区域截屏演示"></a>矩形区域截屏演示</h3><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/rect_screenshot.gif" alt="Long Screenshot"></p><h2 id="视频转换成GIF"><a href="#视频转换成GIF" class="headerlink" title="视频转换成GIF"></a>视频转换成GIF</h2><h3 id="操作方式-5"><a href="#操作方式-5" class="headerlink" title="操作方式"></a>操作方式</h3><p> 进入<strong>Capturer</strong>,选择视频TAB页,点击任何需要转换的视频,然后再点击播放按钮开始播放编辑视频。</p><p> 通过底部拖动按钮选取需要转换视频的时间。</p><p> 点击<code>生成GIF</code>按钮即可弹出选择GIF画质的对话框,选择一个画质点击确定即可开始转换。</p><h3 id="注意事项-4"><a href="#注意事项-4" class="headerlink" title="注意事项"></a>注意事项</h3><p> <strong>第一次开始转换时,需要下载一个<code>FFmpeg</code>动态库</strong>。<br> <strong>选取转换的时间只能精确到秒,因此可能生成GIF图前后会存在少许误差</strong>。<br> <strong>精细画质宽480,帧率12,标准画质360,帧率10,压缩画质宽240,帧率8,为减少用户操作,暂时固定这几种画质</strong>。<br> <strong>画质越高,选择视频时间越长,生成GIF文件也就越大</strong>。</p><h3 id="视频转换成GIF演示"><a href="#视频转换成GIF演示" class="headerlink" title="视频转换成GIF演示"></a>视频转换成GIF演示</h3><p><img src="https://raw.githubusercontent.com/way1989/Captain/master/help/video_to_gif.gif" alt="Long Screenshot"></p>]]></content>
<categories>
<category> 帮助文档 </category>
<category> Capturer </category>
</categories>
<tags>
<tag> 帮助文档 </tag>
</tags>
</entry>
</search>
1
https://gitee.com/way/way.git
git@gitee.com:way/way.git
way
way
way
master

搜索帮助