<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>杨浩的个人博客【yhthu.com】</title>
  <subtitle>分享交流Android、CV、ML、NN等方面的技术和设计思想</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yhthu.com/"/>
  <updated>2016-09-22T10:08:41.481Z</updated>
  <id>http://yhthu.com/</id>
  
  <author>
    <name>yhthu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Android App的架构设计：从VM、MVC、MVP到MVVM</title>
    <link href="http://yhthu.com/2016/09/22/201609221/"/>
    <id>http://yhthu.com/2016/09/22/201609221/</id>
    <published>2016-09-22T10:08:29.146Z</published>
    <updated>2016-09-22T10:08:41.481Z</updated>
    
    <content type="html">&lt;p&gt;随着Android应用开发规模的扩大，客户端业务逻辑也越来越复杂，已然不是简单的数据展示了。如同后端开发遇到瓶颈时采用的组件拆分思想，客户端也需要进行架构设计，拆分视图和数据，解除模块之间的耦合，提高模块内部的聚合度。&lt;/p&gt;
&lt;p&gt;开始之前先上一张内部分享时用的PPT图：&lt;br&gt;&lt;img src=&quot;/img/20160922-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;以上是笔者在客户端开发过程中面临的问题，涉及到以下四个主题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Android App的架构设计：从VM、MVC、MVP到MVVM&lt;/li&gt;
&lt;li&gt;Android App的网络访问：支持REST、HTTPS及SPDY的Retrofit+Okhttp&lt;/li&gt;
&lt;li&gt;Android App的响应式编程：RxJava/RxAndroid解决方案&lt;/li&gt;
&lt;li&gt;Android App的依赖注入：Dagger2和ButterKnife使用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文将从架构设计入手，分享笔者在Android开发中采用MVC、MVP及MVVM的一些想法。&lt;/p&gt;
&lt;h2 id=&quot;一、Android原生开发中的MVC&quot;&gt;&lt;a href=&quot;#一、Android原生开发中的MVC&quot; class=&quot;headerlink&quot; title=&quot;一、Android原生开发中的MVC&quot;&gt;&lt;/a&gt;一、Android原生开发中的MVC&lt;/h2&gt;&lt;p&gt;Android原生开发采用XML文件实现页面布局，通过Java在Activity中开发业务逻辑，这种开发模式实际上已经采用了MVC的思想，分离视图和控制器。MVC模式（Model–view–controller）是软件工程中的一种软件架构模式，把软件系统分为三个基本部分：模型（Model）、视图（View）和控制器（Controller）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MVC模式最早由Trygve Reenskaug在1978年提出，是施乐帕罗奥多研究中心（Xerox&lt;br&gt;PARC）在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程序设计，使后续对程序的修改和扩展简化，并且使程序某一部分的重复利用成为可能。除此之外，此模式通过对复杂度的简化，使程序结构更加直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以通过自身的专长分组：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制器（Controller）- 负责转发请求，对请求进行处理。&lt;/li&gt;
&lt;li&gt;视图（View） - 界面设计人员进行图形界面设计。&lt;/li&gt;
&lt;li&gt;模型（Model） - 程序员编写程序应有的功能（实现算法等等）、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;——以上内容来自《维基百科》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在Android编程中，View对应xml布局文件，Model对应实体模型（网络、数据库、I/O），Controller对应Activity业务逻辑，数据处理和UI处理。如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160922-2.png&quot; alt=&quot;图片来自互联网&quot;&gt;&lt;/p&gt;
&lt;p&gt;但在实际开发过程中，纯粹作为View的各个XML文件功能较弱，Activity基本上都是View和Controller的合体，既要负责视图的显示又要加入控制逻辑，承担的功能很多，导致代码量很大。所有更贴切的目前常规的开发说应该是View-Model模式，大部分都是通过Activity的协调，连接处理逻辑的。&lt;/p&gt;
&lt;h2 id=&quot;二、从MVC过渡到MVP&quot;&gt;&lt;a href=&quot;#二、从MVC过渡到MVP&quot; class=&quot;headerlink&quot; title=&quot;二、从MVC过渡到MVP&quot;&gt;&lt;/a&gt;二、从MVC过渡到MVP&lt;/h2&gt;&lt;p&gt;在业务逻辑稍微复杂一点的页面，Activity的代码超过一千是很容易的，如果作者又刚好读过&lt;a href=&quot;http://coolshell.cn/articles/4758.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;《如何写出无法维护的代码》&lt;/a&gt;，那么恭喜后来接手该代码的童鞋，接下来的几个月会很酸爽的。。。&lt;/p&gt;
&lt;p&gt;既然Activity存在代码量过大的问题，那自然会想到进行拆分。上节说到Android原生开发采用了MVC的思想，但Activity并不是一个标准的MVC模式中的Controller，它的首要职责是加载应用的布局和初始化用户界面，并接受并处理来自用户的操作请求，进而作出响应。随着界面及其逻辑的复杂度不断提升，Activity类的职责不断增加，以致变得庞大臃肿。&lt;/p&gt;
&lt;p&gt;MVP是从MVC过渡而来，MVP框架由三部分组成：View负责显示，Presenter负责逻辑处理，Model提供数据。Android开发从MVC过渡到MVP，最主要的变化就是将Activity中负责业务逻辑的代码移到Presenter中，Activity只充当MVP中的View，负责界面初始化以及建立界面控件与Presenter的关联。&lt;/p&gt;
&lt;p&gt;这样拆分之后，Presenter承担了大量的逻辑操作，避免了Activity的臃肿。整个架构如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160922-3.jpg&quot; alt=&quot;图片来自互联网&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View(Activity)负责响应用户操作，通过Presenter暴露的方法请求数据；&lt;/li&gt;
&lt;li&gt;Presenter在获取数据后，通过View(Activity)暴露的方法实现界面控制（showLoading/showUsers）；&lt;/li&gt;
&lt;li&gt;Presenter的数据是通过Model来获取的，Model包含网络、数据库以及I/O等；&lt;/li&gt;
&lt;li&gt;Model通过回调的方式将数据传到Presenter中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;采用MVP明显的优点是避免了传统开发模式中View和Model耦合的情况，提高了代码可扩展性、组件复用能力、团队协作的效率以及单元测试的便利性。但也有一些缺点，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model到Presenter的数据传递过程需要通过回调；&lt;/li&gt;
&lt;li&gt;View(Activity)需要持有Presenter的引用，同时，Presenter也需要持有View(Activity)的引用，增加了控制的复杂度；&lt;/li&gt;
&lt;li&gt;MVC中Activity的代码很臃肿，转移到MVP的Presenter中，同样造成了Presenter在业务逻辑复杂时的代码臃肿。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;三、从Presenter到ViewModel&quot;&gt;&lt;a href=&quot;#三、从Presenter到ViewModel&quot; class=&quot;headerlink&quot; title=&quot;三、从Presenter到ViewModel&quot;&gt;&lt;/a&gt;三、从Presenter到ViewModel&lt;/h2&gt;&lt;p&gt;MVVM是Model-View-ViewModel的简称，从实际效果来看，ViewModel是View的数据模型和Presenter的结合，具体结构如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160922-4.jpg&quot; alt=&quot;图片来自互联网&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View（视图层）采用XML文件进行界面的描述；&lt;/li&gt;
&lt;li&gt;Model（模型层）通过网络和本地数据库获取视图层所需数据；&lt;/li&gt;
&lt;li&gt;ViewModel（视图-模型层）负责View和Model之间的通信，以此分离视图和数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;View和Model之间通过Android Data Binding技术，实现视图和数据的双向绑定；ViewModel持有Model的引用，通过Model的方法请求数据；获取数据后，通过Callback（回调）的方式回到ViewModel中，由于ViewModel与View的双向绑定，使得界面得以实时更新。同时，界面输入的数据变化时，由于双向绑定技术，ViewModel中的数据得以实时更新，提高了数据采集的效率。&lt;/p&gt;
&lt;p&gt;采用ViewModel解决MVP中View(Activity)和Presenter相互持有对方应用的问题，界面由数据进行驱动，响应界面操作无需由View(Activity)传递，数据的变化也无需Presenter调用View(Activity)实现，使得数据传递的过程更加简洁，高效。&lt;/p&gt;
&lt;p&gt;在Android中实现MVVM架构的核心支撑技术是Google去年I/O大会开源的Data binding技术，这项技术的思想并不新颖，最初由微软提出，在前端开发中已经有成熟的应用。下面对其进行简要的介绍。&lt;/p&gt;
&lt;h2 id=&quot;四、Android中的Data-Binding&quot;&gt;&lt;a href=&quot;#四、Android中的Data-Binding&quot; class=&quot;headerlink&quot; title=&quot;四、Android中的Data Binding&quot;&gt;&lt;/a&gt;四、Android中的Data Binding&lt;/h2&gt;&lt;p&gt;学习Data Binding主要推荐两个内容：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/data-binding/index.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;官方的Data Binding教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/LyndonChin/MasteringAndroidDataBinding&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;精通 Android Data Binding&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这两篇文章中已经将Data Binding的基本内容描述的很详细了。这里仅列两个在实践中遇到的坑，抛砖引玉。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ObservableField的get方法可能无法返回界面实时更新的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public ObservableField&amp;lt;String&amp;gt; username = new ObservableField&amp;lt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;上述username表示用户名，在界面上可能会与EditText绑定。通过username的set方法可以设置EditText显示，但如果输入变更后，通过get方法却不一定能及时返回界面的数据。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data Binding依然有很多支持的不好的组件（listview，recyclerView等），不可能通过给所有组件绑定ViewModel从而实现Activity没有业务逻辑操作。另外，ViewModel获取数据之后，也不可能把所有数据直接绑定到界面，有些也需要通过回调传到Activity中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从MVC、MVP到MVVM，实际上是模型和视图的分离过程。MVC中模型和视图没有完全分离，造成Activity代码臃肿，MVP中通过Presenter来进行中转，模型和视图彻底分离，但由于V和P互相引用，代码不够优雅。ViewModel通过Data Binding实现了视图和数据的绑定，解决了这种MVP的缺陷，但目前也存在Data Binding还不成熟的问题。&lt;/p&gt;
&lt;p&gt;其实，MVC、MVP及MVVM没有绝对好坏，在软件编程过程中，也没必要非此即彼，最重要的是让软件高内聚、低耦合、可维护、可扩展，至于架构，根据实际情况选择吧。&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;随着Android应用开发规模的扩大，客户端业务逻辑也越来越复杂，已然不是简单的数据展示了。如同后端开发遇到瓶颈时采用的组件拆分思想，客户端也需要进行架构设计，拆分视图和数据，解除模块之间的耦合，提高模块内部的聚合度。&lt;/p&gt;
&lt;p&gt;开始之前先上一张内部分享时用的PPT图：
    
    </summary>
    
      <category term="Android开发笔记" scheme="http://yhthu.com/categories/Android%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="Android架构" scheme="http://yhthu.com/tags/Android%E6%9E%B6%E6%9E%84/"/>
    
      <category term="MVVM" scheme="http://yhthu.com/tags/MVVM/"/>
    
      <category term="MVC" scheme="http://yhthu.com/tags/MVC/"/>
    
  </entry>
  
  <entry>
    <title>Android开发笔记——内存泄露与线程安全</title>
    <link href="http://yhthu.com/2016/05/16/201605161/"/>
    <id>http://yhthu.com/2016/05/16/201605161/</id>
    <published>2016-05-16T03:01:44.185Z</published>
    <updated>2016-05-16T03:01:50.141Z</updated>
    
    <content type="html">&lt;p&gt;本次分享主要结合上周Coverity扫描盘古源码发现的问题和平时的实践，对&lt;strong&gt;内存泄露&lt;/strong&gt;和&lt;strong&gt;线程安全&lt;/strong&gt;这两个问题进行一些说明。扫描发现的BUG大致分为四类：1）空指针；2）除0；3）内存、资源泄露；4）线程安全。第一、二个问题属于编码考虑不周，第三、四个问题则需要更深入的分析。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内存泄露&lt;/li&gt;
&lt;li&gt;线程安全&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、内存泄露&quot;&gt;&lt;a href=&quot;#一、内存泄露&quot; class=&quot;headerlink&quot; title=&quot;一、内存泄露&quot;&gt;&lt;/a&gt;一、内存泄露&lt;/h2&gt;&lt;h3 id=&quot;1、很抱歉，”XXX”已停止运行。OOM？&quot;&gt;&lt;a href=&quot;#1、很抱歉，”XXX”已停止运行。OOM？&quot; class=&quot;headerlink&quot; title=&quot;1、很抱歉，”XXX”已停止运行。OOM？&quot;&gt;&lt;/a&gt;1、很抱歉，”XXX”已停止运行。OOM？&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;/img/20160509-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;怎样才能让app报OOM呢？最简单的办法如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Bitmap bt1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.image);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Bitmap bt2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.image);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Bitmap btn = ...&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id=&quot;2、查看内存占用&quot;&gt;&lt;a href=&quot;#2、查看内存占用&quot; class=&quot;headerlink&quot; title=&quot;2、查看内存占用&quot;&gt;&lt;/a&gt;2、查看内存占用&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;命令行：adb shell dumpsys meminfo &lt;strong&gt;packageName&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160509-2.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过Android Studio的Memory Monitor查看内存中Dalvik Heap的实时变化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160507-2.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3、发生内存泄露的条件&quot;&gt;&lt;a href=&quot;#3、发生内存泄露的条件&quot; class=&quot;headerlink&quot; title=&quot;3、发生内存泄露的条件&quot;&gt;&lt;/a&gt;3、发生内存泄露的条件&lt;/h3&gt;&lt;p&gt;首先，每个app有最大内存限制。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;ActivityManager activityManager = (ActivityManager)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        context.getSystemService(Context.ACTIVITY_SERVICE);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;activityManager.getMemoryClass();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;getMemoryClass()取到的是最大内存资源。Android中的堆内存分为Native Heap和Dalvik Heap。C/C++申请的内存空间在Native Heap中，Java申请的内存空间则在Dalvik Heap中。对于head堆的大小限制，可以查看/system/build.prop文件：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;dalvik.vm.heapstartsize=8m&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;dalvik.vm.heapgrowthlimit=96m&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;dalvik.vm.heapsize=256m&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;注意：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;heapsize参数表示单个进程heap可用的最大内存，但如果存在以下参数”dalvik.vm.headgrowthlimit =96m”表示单个进程heap内存被限定在96m，即程序运行过程实际只能使用96m内存。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果申请的内存资源超过上述限制，系统就会抛出OOM错误。&lt;/p&gt;
&lt;h3 id=&quot;4、常见避免OOM的措施&quot;&gt;&lt;a href=&quot;#4、常见避免OOM的措施&quot; class=&quot;headerlink&quot; title=&quot;4、常见避免OOM的措施&quot;&gt;&lt;/a&gt;4、常见避免OOM的措施&lt;/h3&gt;&lt;p&gt;以下主要从四个方面总结常见的措施：1）减小对象的内存占用；2）内存对象的重复利用；3）避免对象的内存泄露；4）内存使用策略优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.1 减小对象的内存占用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用ArrayMap/SparseArray而不是HashMap等传统数据结构。&lt;a href=&quot;http://blog.csdn.net/u010687392/article/details/47809295&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;参考链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;在Android中避免使用枚举。&lt;/li&gt;
&lt;li&gt;减小Bitmap对象的内存占用。inSampleSize和decode format。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4.2 内存对象的重复利用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ListView/GridView等出现大量重复子组件的视图里面对ConvertView的复用&lt;/li&gt;
&lt;li&gt;使用LRU机制缓存Bitmap&lt;/li&gt;
&lt;li&gt;避免在onDraw方法里面执行对象的创建&lt;/li&gt;
&lt;li&gt;使用StringBuilder来替代频繁的”+”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4.3 避免对象的内存泄露&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;4.1和4.2都是比较常规的措施，4.3需要重点关注。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1）Activity泄露&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;导致Activity泄露的原因较多，下面列举一些比较常见的。从原理上主要分为两类：&lt;strong&gt;i）静态对象；ii）this$0&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Activity被static变量引用。这段代码来自于我们的&lt;strong&gt;Crash上传&lt;/strong&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private static Map&amp;lt;ComponentName, ExceptionHandler&amp;gt; configMap = &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        new HashMap&amp;lt;ComponentName, ExceptionHandler&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static void setActivity(final Activity activity, boolean send2Server) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Log.d(TAG, &amp;quot;bind exception handler : &amp;quot; + activity.getComponentName().getClassName());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    //上下文初始化&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    SDKContext.init(activity.getApplication());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    init(activity.getApplication());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ExceptionHandler exceptionHandler = new ExceptionHandler(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        activity, send2Server, Thread.getDefaultUncaughtExceptionHandler());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    configMap.put(activity.getComponentName(), exceptionHandler);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面是通过MAT分析“服务+”中一个Activity泄露的截图：&lt;br&gt;&lt;img src=&quot;/img/20160512-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内部类引用导致Activity的泄漏&lt;br&gt;最典型的场景是Handler导致的Activity泄漏，如果Handler中有延迟的任务或者是等待执行的任务队列过长，都有可能因为Handler继续执行而导致Activity发生泄漏。此时的引用关系链是&lt;strong&gt;Looper -&amp;gt; MessageQueue -&amp;gt; Message -&amp;gt; Handler -&amp;gt; Activity&lt;/strong&gt;。为了解决这个问题，可以在UI退出之前，执行remove Handler消息队列中的消息与runnable对象。或者是使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。&lt;br&gt;可参考链接：&lt;a href=&quot;http://www.yhthu.com/2016/05/03/Android%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;线程通信&lt;/a&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Handler mHandler = new Handler() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        super.handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 根据msg调用Activity的方法&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;static class MyHandler extends Handler &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    WeakReference&amp;lt;DemoActivity&amp;gt; mActivity;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public MyHandler(DemoActivity demoActivity) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mActivity = new WeakReference&amp;lt;DemoActivity&amp;gt;(demoActivity);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        super.handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        DemoActivity theActivity = mActivity.get();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 根据msg调用theActivity的方法&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;2）考虑使用Application Context而不是Activity Context&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对于大部分非必须使用Activity Context的情况（Dialog的Context就必须是Activity Context），我们都可以考虑使用Application Context而不是Activity的Context，这样可以避免不经意的Activity泄露。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3）注意临时Bitmap对象的及时回收&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;虽然在大多数情况下，我们会对Bitmap增加缓存机制，但是在某些时候，部分Bitmap是需要及时回收的。例如临时创建的某个相对比较大的bitmap对象，在经过变换得到新的bitmap对象之后，应该尽快回收原始的bitmap，这样能够更快释放原始bitmap所占用的空间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4）内存占用监控&lt;/strong&gt;&lt;br&gt;通过Runtime获取maxMemory，而maxMemory-totalMemory即为剩余可使用的dalvik内存。定期检查这个值，达到80%就去释放各种cache资源（bitmap的cache）。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Returns the maximum number of bytes the heap can expand to. See &amp;#123;@link #totalMemory&amp;#125; for the&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * current number of bytes taken by the heap, and &amp;#123;@link #freeMemory&amp;#125; for the current number of&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * those bytes actually used by live objects.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int maxMemory = Runtime.getRuntime().maxMemory()); // 应用程序最大可用内存&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Returns the number of bytes taken by the heap at its current size. The heap may expand or&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * contract over time, as the number of live objects increases or decreases. See&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;#123;@link #maxMemory&amp;#125; for the maximum heap size, and &amp;#123;@link #freeMemory&amp;#125; for an idea of how much&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * the heap could currently contract.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;long totalMemory = Runtime.getRuntime().totalMemory()); // 应用程序已获得内存&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Returns the number of bytes currently available on the heap without expanding the heap. See&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;#123;@link #totalMemory&amp;#125; for the heap&amp;apos;s current size. When these bytes are exhausted, the heap&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * may expand. See &amp;#123;@link #maxMemory&amp;#125; for that limit.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;long freeMemory = Runtime.getRuntime().freeMemory()); // 应用程序已获得内存中未使用内存&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5）注意Cursor对象是否及时关闭&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在程序中我们经常会进行查询数据库的操作，但时常会存在不小心使用Cursor之后没有及时关闭的情况。这些Cursor的泄露，反复多次出现的话会对内存管理产生很大的负面影响，我们需要谨记对Cursor对象的及时关闭。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.4 内存使用策略优化&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;谨慎使用large heap&lt;/li&gt;
&lt;li&gt;综合考虑设备内存阈值与其他因素设计合适的缓存大小&lt;/li&gt;
&lt;li&gt;onLowMemory()/onTrimMemory(int)&lt;/li&gt;
&lt;li&gt;资源文件需要选择合适的文件夹进行存放&lt;/li&gt;
&lt;li&gt;Try catch某些大内存分配的操作&lt;/li&gt;
&lt;li&gt;谨慎使用static对象&lt;/li&gt;
&lt;li&gt;优化布局层次，减少内存消耗&lt;/li&gt;
&lt;li&gt;谨慎使用多进程&lt;/li&gt;
&lt;li&gt;谨慎使用依赖注入框架&lt;/li&gt;
&lt;li&gt;使用ProGuard来剔除不需要的代码&lt;/li&gt;
&lt;li&gt;谨慎使用第三方libraries&lt;/li&gt;
&lt;li&gt;考虑不同的实现方式来优化内存占用&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;二、线程安全&quot;&gt;&lt;a href=&quot;#二、线程安全&quot; class=&quot;headerlink&quot; title=&quot;二、线程安全&quot;&gt;&lt;/a&gt;二、线程安全&lt;/h2&gt;&lt;h3 id=&quot;1、下面的方法是线程安全的吗？&quot;&gt;&lt;a href=&quot;#1、下面的方法是线程安全的吗？&quot; class=&quot;headerlink&quot; title=&quot;1、下面的方法是线程安全的吗？&quot;&gt;&lt;/a&gt;1、下面的方法是线程安全的吗？&lt;/h3&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;class MyCounter &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	private static int counter = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	public static int getCount() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;		return counter++;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;怎样使上述方法线程安全？&lt;/p&gt;
&lt;h3 id=&quot;2、Java中的线程安全&quot;&gt;&lt;a href=&quot;#2、Java中的线程安全&quot; class=&quot;headerlink&quot; title=&quot;2、Java中的线程安全&quot;&gt;&lt;/a&gt;2、Java中的线程安全&lt;/h3&gt;&lt;p&gt;怎样保持在多线程环境下的数据一致性，Java提供了多种方法实现：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;synchronized&lt;/li&gt;
&lt;li&gt;java.util.concurrent.atomic&lt;/li&gt;
&lt;li&gt;java.util.concurrent.locks&lt;/li&gt;
&lt;li&gt;thread safe collection（ConcurrentHashMap）&lt;/li&gt;
&lt;li&gt;volatile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;2.1 synchronized&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JVM保证被synchronized关键字修饰的代码段在同一时间只能被一个线程访问，内部通过对&lt;strong&gt;对象&lt;/strong&gt;或&lt;strong&gt;类&lt;/strong&gt;加锁来实现的。当方法被synchronized修饰时，锁加在对象上；当方法同时为static时，锁加在类上。从性能的角度来讲，一般不建议直接将锁加在类上，这样会使得类的所有对象的该方法均为synchronized的。&lt;/p&gt;
&lt;p&gt;从之前扫描的问题来看，在编写synchronized程序时主要有两点需要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;synchronized需要创建基于对象或者类的锁，所以不能在构造器或者变量上加锁。&lt;/li&gt;
&lt;li&gt;synchronized造成死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;1) 锁加在哪里？&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;List&amp;lt;ResultPoint&amp;gt; currentPossible = possibleResultPoints;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;List&amp;lt;ResultPoint&amp;gt; currentLast = lastPossibleResultPoints;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int frameLeft = frame.left;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int frameTop = frame.top;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if (currentPossible.isEmpty()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	lastPossibleResultPoints = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	possibleResultPoints = new ArrayList&amp;lt;&amp;gt;(5);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	lastPossibleResultPoints = currentPossible;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	paint.setAlpha(CURRENT_POINT_OPACITY);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	paint.setColor(resultPointColor);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	synchronized (currentPossible) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;		for (ResultPoint point : currentPossible) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;			canvas.drawCircle(frameLeft&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;					+ (int) (point.getX() * scaleX), frameTop&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;					+ (int) (point.getY() * scaleY), POINT_SIZE,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;					paint);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;		&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;上述方法中，possibleResultPoints的创建没有采用同步措施，需要使用&lt;strong&gt;Collections.synchronizedXxx&lt;/strong&gt;。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;List&amp;lt;MyType&amp;gt; list = Collections.synchronizedList(new ArrayList(&amp;lt;MyType&amp;gt;));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;synchronized(list)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    for(MyType m : list)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        foo(m);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        m.doSomething();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;一般比较推荐创建一个虚拟的对象专门用于获取锁。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;//dummy object variable for synchronization&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private Object mutex=new Object();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;//using synchronized block to read, increment and update count value synchronously&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;synchronized (mutex) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        count++;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;PS：直接在方法上加synchronized可能DoS攻击喔，举个栗子：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class MyObject &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Locks on the object&amp;apos;s monitor&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public synchronized void doSomething() &amp;#123; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // ...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 黑客的代码&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;MyObject myObject = new MyObject();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;synchronized (myObject) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    while (true) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Indefinitely delay myObject&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread.sleep(Integer.MAX_VALUE); &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;黑客的代码获取了MyObject对象的锁，导致doSomething死锁，从而引发Denial of Service。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class MyObject &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    //locks on the class object&amp;apos;s monitor&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public static synchronized void doSomething() &amp;#123; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // ...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 黑客的代码&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;synchronized (MyObject.class) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    while (true) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2) 死锁&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;56&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class ThreadDeadlock &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public static void main(String[] args) throws InterruptedException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Object obj1 = new Object();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Object obj2 = new Object();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Object obj3 = new Object();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread t1 = new Thread(new SyncThread(obj1, obj2), &amp;quot;t1&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread t2 = new Thread(new SyncThread(obj2, obj3), &amp;quot;t2&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread t3 = new Thread(new SyncThread(obj3, obj1), &amp;quot;t3&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        t1.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread.sleep(5000);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        t2.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Thread.sleep(5000);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        t3.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;class SyncThread implements Runnable&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Object obj1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Object obj2;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public SyncThread(Object o1, Object o2)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        this.obj1=o1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        this.obj2=o2;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        String name = Thread.currentThread().getName();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        System.out.println(name + &amp;quot; acquiring lock on &amp;quot;+obj1);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        synchronized (obj1) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         System.out.println(name + &amp;quot; acquired lock on &amp;quot;+obj1);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         work();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         System.out.println(name + &amp;quot; acquiring lock on &amp;quot;+obj2);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         synchronized (obj2) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            System.out.println(name + &amp;quot; acquired lock on &amp;quot;+obj2);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            work();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         System.out.println(name + &amp;quot; released lock on &amp;quot;+obj2);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        System.out.println(name + &amp;quot; released lock on &amp;quot;+obj1);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        System.out.println(name + &amp;quot; finished execution.&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private void work() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            Thread.sleep(30000);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; catch (InterruptedException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;上述代码会输出什么呢？&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;t1 acquiring lock on java.lang.Object@6d9dd520&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t1 acquired lock on java.lang.Object@6d9dd520&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t2 acquiring lock on java.lang.Object@22aed3a5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t2 acquired lock on java.lang.Object@22aed3a5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t3 acquiring lock on java.lang.Object@218c2661&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t3 acquired lock on java.lang.Object@218c2661&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t1 acquiring lock on java.lang.Object@22aed3a5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t2 acquiring lock on java.lang.Object@218c2661&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;t3 acquiring lock on java.lang.Object@6d9dd520&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;本次分享主要结合上周Coverity扫描盘古源码发现的问题和平时的实践，对&lt;strong&gt;内存泄露&lt;/strong&gt;和&lt;strong&gt;线程安全&lt;/strong&gt;这两个问题进行一些说明。扫描发现的BUG大致分为四类：1）空指针；2）除0；3）内存、资源泄露；4）线程安全。第一
    
    </summary>
    
      <category term="Android内存管理" scheme="http://yhthu.com/categories/Android%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/"/>
    
    
      <category term="Android内存管理" scheme="http://yhthu.com/tags/Android%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/"/>
    
      <category term="进程内存" scheme="http://yhthu.com/tags/%E8%BF%9B%E7%A8%8B%E5%86%85%E5%AD%98/"/>
    
  </entry>
  
  <entry>
    <title>读书笔记-Autonomous Intelligent Vehicles（一）</title>
    <link href="http://yhthu.com/2016/05/16/201605201/"/>
    <id>http://yhthu.com/2016/05/16/201605201/</id>
    <published>2016-05-16T01:19:52.006Z</published>
    <updated>2016-05-16T01:20:03.076Z</updated>
    
    <content type="html">&lt;p&gt;Autonomous intelligent vehicles have to finish the basic procedures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;perceiving and modeling environment&lt;/li&gt;
&lt;li&gt;localizing and building maps&lt;/li&gt;
&lt;li&gt;planning paths and making decisions&lt;/li&gt;
&lt;li&gt;controlling the vehicles within limit time for real-time purposes. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Meanwhile, we face the challenge of processing large amounts of data from multi-sensors, such as cameras, lidars, radars.&lt;/p&gt;
&lt;p&gt;Our goal in writing this book is threefold:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, it creates an updated reference book of intelligent vehicles. &lt;/li&gt;
&lt;li&gt;Second, this book not only presents object/obstacle detection and recognition, but also introduces vehicle lateral and longitudinal control algorithms, which benefits the readers keen to learn broadly about intelligent vehicles.&lt;/li&gt;
&lt;li&gt;Finally, we put emphasis on high-level concepts, and at the same time provide the low-level details of implementation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We try to link theory, algorithms, and implementation to promote intelligent vehicle research. &lt;/p&gt;
&lt;p&gt;This book is divided into four parts. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first part &lt;strong&gt;Autonomous Intelligent Vehicles&lt;/strong&gt; presents the research motivation and purposes, the state-of-art of intelligent vehicles research. Also, we introduce the framework of intelligent vehicles. &lt;/li&gt;
&lt;li&gt;The second part &lt;strong&gt;Environment Perception and Modeling&lt;/strong&gt; which includes Road detection and tracking, Vehicle detection and tracking, Multiple-sensor based multiple-object tracking introduces environment perception and modeling. &lt;/li&gt;
&lt;li&gt;The third part &lt;strong&gt;Vehicle Localization and Navigation&lt;/strong&gt; which includes An integrated DGPS/IMU positioning approach, Vehicle navigation using global views presents vehicle navigation based on integrated GPS and INS. &lt;/li&gt;
&lt;li&gt;The fourth part &lt;strong&gt;Advanced Vehicle Motion Control&lt;/strong&gt; introduces vehicle lateral and longitudinal motion control.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;The Key Technologies of Intelligent Vehicles:&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/img/20160514-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-sensor Fusion Based Environment Perception and Modeling&lt;/li&gt;
&lt;li&gt;Vehicle Localization and Map Building&lt;/li&gt;
&lt;li&gt;Path Planning and Decision-Making&lt;/li&gt;
&lt;li&gt;Low-Level Motion Control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基于环境认知与建模的多维传感器数据融合&lt;br&gt;&lt;img src=&quot;/img/20160514-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;Figure 1.2 illustrates a general environment perception and modeling framework. From this framework, we can see that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(i) The original data are collected by various sensors; &lt;/li&gt;
&lt;li&gt;(ii) Various features are extracted from the original data, such as road (object) colors, lane edges, building contours; &lt;/li&gt;
&lt;li&gt;(iii) Semantic objects are recognized using classifiers, and consist of lanes, signs, vehicles, pedestrians; &lt;/li&gt;
&lt;li&gt;(iv) We can deduce driving contexts, and vehicle positions.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Multi-sensor fusion&lt;br&gt;Multi-sensor fusion is the basic framework of intelligent vehicles for better sensing surrounding environment structures, and detecting objects/obstacles. Roughly, the sensors used for surrounding environment perception are divided into two categories: &lt;strong&gt;active and passive ones. Active sensors include lidar, radar, ultrasonic and radio, while the commonly-used passive sensors are infrared and visual cameras.&lt;/strong&gt; Different sensors are capable of providing different detection precision and range, and yielding different effects on environment. That is, combining various sensors could cover not only short-range but also long-range objects/obstacles, and also work in various weather conditions. Furthermore, the original data of different sensors can be fused in low-level fusion, high-level fusion, and hybrid fusion.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic Environment Modeling&lt;br&gt;Dynamic environment modeling based on moving on-vehicle cameras plays an important role in intelligent vehicles [17]. However, this is extremely challenging due to the combined effects of &lt;strong&gt;ego-motion, blur, light changing&lt;/strong&gt;. Therefore, traditional methods for gradual illumination change, small motion objects, such as background subtraction, do not work well any more, even those that have been widely used in surveillance applications. Consequently, more and more approaches try to handle these issues [2, 17]. Unfortunately, it is still an open problem to reliably model and update background. To select different driving strategies, several broad scenarios are usually considered in path planning and decision-making, when navigating roads, intersections, parking lots, jammed intersections. Hence, scenario estimators are helpful for further decision-making, which is commonly used in the Urban Challenge.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Object Detection and Tracking&lt;br&gt;In general, in a driving environment, we are interested in static/dynamic obstacles, lane markings, traffic signs, vehicles, and pedestrians. Correspondingly, object detection and tracking are the key parts of environment perception and modeling.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;通过多维传感器数据融合，有效实现对短距离、长距离的物体/障碍物的识别、跟踪，从而达到对环境的建模。可以看出，计算机视觉仍然是动态环境建模的挑战。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;高精度定位和地图的构建&lt;/p&gt;
&lt;p&gt;The goal of vehicle localization and map building is to &lt;strong&gt;generate a global map&lt;/strong&gt; by &lt;strong&gt;combining the environment model, a local map and global information&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For vehicle localization, we face several challenges as follows: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(i) Usually, the absolute positions from GPS/DGPS and its variants are insufficient due to signal transmission; &lt;/li&gt;
&lt;li&gt;(ii) The path planning and decision-making module needs more than just the vehicle absolute position as input; &lt;/li&gt;
&lt;li&gt;(iii) Sensor noises greatly affect the accuracy of vehicle localization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160514-3.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Regarding the first issue, though the GPS and its variants have been widely used in vehicle localization, its performance could degrade due to signal blockages and reflections of buildings and trees. In the worst case, Inertia Navigation System (INS) can maintain a position solution.&lt;/p&gt;
&lt;p&gt;As for the second issue, local maps fusing laser, radar, and vision data with vehicle states are used to locate and track both static/dynamic obstacles and lanes. Furthermore, global maps could contain lane geometric information, lane makings, step signs, parking lots, check points and provide global environment information. &lt;/p&gt;
&lt;p&gt;Referring to the third issue, various noise modules are considered to reduce localization error.&lt;/p&gt;
&lt;p&gt;SLAM是目前研究的比较多的机器人定位和地图构建的算法。下面是结合ROS和SLAM的一些展示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160514-4.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;img src=&quot;/img/20160514-5.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;img src=&quot;/img/20160514-6.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;img src=&quot;/img/20160514-7.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;img src=&quot;/img/20160514-8.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;路径规划和决策制定&lt;/p&gt;
&lt;p&gt;Global path planning is to find the fastest and safest way to get from the initial position to the goal position, while local path planning is to avoid obstacles for safe navigation. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Road following, making lane-changes, parking, obstacle avoidance, recovering from abnormal conditions.&lt;/strong&gt; In many cases, decision-making depends of context driving, especially in driver assistance systems.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160514-9.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;目前在高德、百度中用到路径规划算法是否可以通用呢？&lt;/p&gt;
&lt;p&gt;低层运动控制&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160514-10.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Its typical applications consist of automatic vehicle following/platoon, Adaptive Cruise Control (ACC), lane following. Vehicle control can be broadly divided into two categories: &lt;strong&gt;lateral control and longitudinal control&lt;/strong&gt;(Fig. 1.4). The longitudinal control is related to d&lt;strong&gt;istance–velocity control&lt;/strong&gt; between vehicles for safety and comfort purposes. Here some assumptions are made about the state of vehicles and the parameters of models, such as in the PATH project. The lateral control isto &lt;strong&gt;maintain the vehicle’s position in the lane center&lt;/strong&gt;, and it can be used for vehicle guidance assistance. Moreover, it is well known that the lateral and longitudinal dynamics of a vehicle are coupled in a combined lateral and longitudinal control, where the coupling degree is a function of the tire and vehicle parameters. In general, there are two different approaches to design vehicle controllers. One way to do this is to mimic driver operations, and the other is based on vehicle dynamic models and control strategies.&lt;/p&gt;
&lt;p&gt;从目前业界动态来看，国内做自动驾驶、无人驾驶创业的厂商大多从ADAS切入，有市场的原因，比如目前普通车主能接受的汽车更加安全、智能，但还没到自动驾驶的程度；有技术的原因，移动设备的数据处理能力以及算法的实时性依然有待提升。如上所述，类似ADAS的运动控制可以分为横向和纵向的控制，横向运动控制主要是使车辆保持在道路中间，如车道保持系统；纵向运动控制基于距离和速度，是行驶安全性、舒适性的关键，自适应巡航、防碰撞预警系统等控制都属于纵向运动控制。&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;Autonomous intelligent vehicles have to finish the basic procedures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;perceiving and modeling environment&lt;/li&gt;
&lt;li&gt;localizing
    
    </summary>
    
      <category term="读书笔记" scheme="http://yhthu.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="Theory, Algorithms, Implementation" scheme="http://yhthu.com/tags/Theory-Algorithms-Implementation/"/>
    
      <category term="Perceiving and modeling environment" scheme="http://yhthu.com/tags/Perceiving-and-modeling-environment/"/>
    
  </entry>
  
  <entry>
    <title>Android源码笔记——Camera系统架构</title>
    <link href="http://yhthu.com/2016/05/03/201605031/"/>
    <id>http://yhthu.com/2016/05/03/201605031/</id>
    <published>2016-05-03T10:28:27.523Z</published>
    <updated>2016-05-03T10:28:34.623Z</updated>
    
    <content type="html">&lt;p&gt;Camera的架构与Android系统的整体架构保持一致，如下图所示，本文主要从以下四个方面对其进行说明。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Framework：Camera.java&lt;/li&gt;
&lt;li&gt;Android Runtime：android_hardware_Camera.cpp&lt;/li&gt;
&lt;li&gt;Library：Camera Client和Camera Service&lt;/li&gt;
&lt;li&gt;HAL：CameraHardwareInterface&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/img/20160401-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、Framework：Camera-java&quot;&gt;&lt;a href=&quot;#一、Framework：Camera-java&quot; class=&quot;headerlink&quot; title=&quot;一、Framework：Camera.java&quot;&gt;&lt;/a&gt;一、Framework：Camera.java&lt;/h2&gt;&lt;p&gt;Camera是应用层软件直接使用的类，涵盖了启动、预览、拍摄及关闭等操作摄像头的全部接口。Camera.java在Android源码中的路径为：framework/base/core/java/android/hardware。为了说明整个Camera系统的架构，这里暂不横向分析Camera.java的功能，下面从open()方法着手：&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static Camera open() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int numberOfCameras = getNumberOfCameras();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    CameraInfo cameraInfo = new CameraInfo();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    for (int i = 0; i &amp;lt; numberOfCameras; i++) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        getCameraInfo(i, cameraInfo);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return new Camera(i);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;open()方法需要注意以下几点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;getNumberOfCameras为native方法，实现在android_hardware_Camera.cpp中；&lt;/li&gt;
&lt;li&gt;CameraInfo是Camera定义的静态内部类，包含facing、orientation、canDisableShutterSound；&lt;/li&gt;
&lt;li&gt;getCameraInfo内部调用native方法_getCameraInfo获取摄像头信息；&lt;/li&gt;
&lt;li&gt;open()默认启动的是后置摄像头（CAMERA_FACING_BACK）。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** used by Camera#open, Camera#open(int) */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Camera(int cameraId) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int err = cameraInitNormal(cameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (checkInitErrors(err)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        switch(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            case EACCESS:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                throw new RuntimeException(&amp;quot;Fail to connect to camera service&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            case ENODEV:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                throw new RuntimeException(&amp;quot;Camera initialization failed&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            default:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                // Should never hit this.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                throw new RuntimeException(&amp;quot;Unknown camera error&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Camera构造器的核心实现在cameraInitNormal中，cameraInitNormal调用cameraInitVersion，并传入参数cameraId和CAMERA_HAL_API_VERSION_NORMAL_CONNECT，后者代表HAL的版本。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private int cameraInitVersion(int cameraId, int halVersion) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String packageName = ActivityThread.currentPackageName();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return native_setup(new WeakReference&amp;lt;Camera&amp;gt;(this), cameraId, halVersion, packageName);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;cameraInitNormal调用本地方法native_setup()，由此进入到android_hardware_Camera.cpp中，native_setup()的签名如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private native final int native_setup(Object camera_this, &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                int cameraId, int halVersion, String packageName);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id=&quot;二、Android-Runtime：android-hardware-Camera-cpp&quot;&gt;&lt;a href=&quot;#二、Android-Runtime：android-hardware-Camera-cpp&quot; class=&quot;headerlink&quot; title=&quot;二、Android Runtime：android_hardware_Camera.cpp&quot;&gt;&lt;/a&gt;二、Android Runtime：android_hardware_Camera.cpp&lt;/h2&gt;&lt;p&gt;native_setup()被动态注册到JNI，通过JNI调用android_hardware_Camera_native_setup()方法。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;static JNINativeMethod camMethods[] = &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#123; &amp;quot;native_setup&amp;quot;,    &amp;quot;(Ljava/lang/Object;ILjava/lang/String;)V&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    (void*)android_hardware_Camera_native_setup &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;JNI的重点是android_hardware_Camera_native_setup()方法的实现：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// connect to camera service&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Convert jstring to String16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    const char16_t *rawClientName = env-&amp;gt;GetStringChars(clientPackageName, NULL);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    jsize rawClientNameLen = env-&amp;gt;GetStringLength(clientPackageName);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String16 clientName(rawClientName, rawClientNameLen);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    env-&amp;gt;ReleaseStringChars(clientPackageName, rawClientName);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sp&amp;lt;Camera&amp;gt; camera;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Default path: hal version is don&amp;apos;t care, do normal camera connect.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        camera = Camera::connect(cameraId, clientName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                Camera::USE_CALLING_UID);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        jint status = Camera::connectLegacy(cameraId, halVersion, clientName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                Camera::USE_CALLING_UID, camera);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (status != NO_ERROR) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return status;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (camera == NULL) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return -EACCES;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // make sure camera hardware is alive&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (camera-&amp;gt;getStatus() != NO_ERROR) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return NO_INIT;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    jclass clazz = env-&amp;gt;GetObjectClass(thiz);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (clazz == NULL) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // This should never happen&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        jniThrowRuntimeException(env, &amp;quot;Can&amp;apos;t find android/hardware/Camera&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return INVALID_OPERATION;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // We use a weak reference so the Camera object can be garbage collected.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // The reference is only used as a proxy for callbacks.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sp&amp;lt;JNICameraContext&amp;gt; context = new JNICameraContext(env, weak_this, clazz, camera);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    context-&amp;gt;incStrong((void*)android_hardware_Camera_native_setup);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    camera-&amp;gt;setListener(context);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // save context in opaque field&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    env-&amp;gt;SetLongField(thiz, fields.context, (jlong)context.get());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return NO_ERROR;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;android_hardware_Camera_native_setup()方法通过调用Camera::connect()方法请求连接CameraService服务。入参中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clientName是通过将clientPackageName从jstring转换为String16格式得到；&lt;/li&gt;
&lt;li&gt;Camera::USE_CALLING_UID是定义在Camera.h中的枚举类型，其值为ICameraService::USE_CALLING_UID（同样为枚举类型，值为-1）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Camera::connect()位于Camera.cpp中，由此进入到Library层。&lt;/p&gt;
&lt;h2 id=&quot;三、Library：Camera-Client和Camera-Service&quot;&gt;&lt;a href=&quot;#三、Library：Camera-Client和Camera-Service&quot; class=&quot;headerlink&quot; title=&quot;三、Library：Camera Client和Camera Service&quot;&gt;&lt;/a&gt;三、Library：Camera Client和Camera Service&lt;/h2&gt;&lt;p&gt;如上述架构图中所示，ICameraService.h、ICameraClient.h和ICamera.h三个类定义了Camera的接口和架构，ICameraService.cpp和Camera.cpp两个文件用于Camera架构的实现，Camera的具体功能在下层调用硬件相关的接口来实现。Camera.h是Camera系统对上层的接口。&lt;/p&gt;
&lt;p&gt;具体的，Camera类继承模板类CameraBase，Camera::connect()调用了CameraBase.cpp中的connect()方法。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sp&amp;lt;Camera&amp;gt; Camera::connect(int cameraId, const String16&amp;amp; clientPackageName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int clientUid) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return CameraBaseT::connect(cameraId, clientPackageName, clientUid);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;CameraBase实际上又继承了IBinder的DeathRecipient内部类，DeathRecipient虚拟继承自RefBase。RefBase是Android中的引用计数基础类，其中定义了incStrong、decStrong、incWeak和decWeak等涉及sp/wp的指针操作函数，当然这扯远了。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;template &amp;lt;typename TCam&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;struct CameraTraits &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;template &amp;lt;typename TCam, typename TCamTraits = CameraTraits&amp;lt;TCam&amp;gt; &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;class CameraBase : public IBinder::DeathRecipient&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    static sp&amp;lt;TCam&amp;gt; connect(int cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            const String16&amp;amp; clientPackageName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            int clientUid);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;class DeathRecipient : public virtual RefBase&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    virtual void binderDied(const wp&amp;lt;IBinder&amp;gt;&amp;amp; who) = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;回到Camera::connect()的实现上，其中，new TCam(cameraId)生成BnCameraClient对象，BnCameraClient定义在ICameraClient.h文件中，继承自模板类BnInterface。getCameraService()方法返回CameraService的服务代理BpCameraService，BpCameraService同样继承自模板类BnInterface。然后通过Binder通信发送CONNECT命令，当BnCameraService收到CONNECT命令后调用CameraService的connect()成员函数来做相应的处理。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;template &amp;lt;typename TCam, typename TCamTraits&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sp&amp;lt;TCam&amp;gt; CameraBase&amp;lt;TCam, TCamTraits&amp;gt;::connect(int cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                               const String16&amp;amp; clientPackageName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                               int clientUid)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ALOGV(&amp;quot;%s: connect&amp;quot;, __FUNCTION__);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sp&amp;lt;TCam&amp;gt; c = new TCam(cameraId); // BnCameraClient &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sp&amp;lt;TCamCallbacks&amp;gt; cl = c;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    status_t status = NO_ERROR;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    const sp&amp;lt;ICameraService&amp;gt;&amp;amp; cs = getCameraService(); // BpCameraService&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (cs != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        TCamConnectService fnConnectService = TCamTraits::fnConnectService;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        status = (cs.get()-&amp;gt;*fnConnectService)(cl, cameraId, clientPackageName, clientUid,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                             /*out*/ c-&amp;gt;mCamera);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (status == OK &amp;amp;&amp;amp; c-&amp;gt;mCamera != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        c-&amp;gt;mCamera-&amp;gt;asBinder()-&amp;gt;linkToDeath(c);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        c-&amp;gt;mStatus = NO_ERROR;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ALOGW(&amp;quot;An error occurred while connecting to camera: %d&amp;quot;, cameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        c.clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return c;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;class BnCameraClient: public BnInterface&amp;lt;ICameraClient&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    virtual status_t    onTransact( uint32_t code,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                    const Parcel&amp;amp; data,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                    Parcel* reply,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                    uint32_t flags = 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;class BpCameraService: public BpInterface&amp;lt;ICameraService&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    BpCameraService(const sp&amp;lt;IBinder&amp;gt;&amp;amp; impl)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        : BpInterface&amp;lt;ICameraService&amp;gt;(impl)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;注：connect()函数在BpCameraService和BnCameraService的父类ICameraService中声明为纯虚函数，在BpCameraService和CameraService中分别给出了实现，BpCameraService作为代理类，提供接口给客户端，真正实现在BnCameraService的子类CameraService中。&lt;/p&gt;
&lt;p&gt;在BpCameraService中，connect()函数实现如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// connect to camera service (android.hardware.Camera)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;virtual status_t connect(const sp&amp;lt;ICameraClient&amp;gt;&amp;amp; cameraClient, int cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                         const String16 &amp;amp;clientPackageName, int clientUid,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                         /*out*/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                         sp&amp;lt;ICamera&amp;gt;&amp;amp; device)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Parcel data, reply;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    data.writeStrongBinder(cameraClient-&amp;gt;asBinder());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    data.writeInt32(cameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    data.writeString16(clientPackageName);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    data.writeInt32(clientUid);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    remote()-&amp;gt;transact(BnCameraService::CONNECT, data, &amp;amp;reply); // BpBinder的transact()函数向IPCThreadState实例发送消息，通知其有消息要发送给binder driver        &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (readExceptionCode(reply)) return -EPROTO;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    status_t status = reply.readInt32();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (reply.readInt32() != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        device = interface_cast&amp;lt;ICamera&amp;gt;(reply.readStrongBinder()); // client端读出server返回的bind&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return status;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;首先将传递过来的Camera对象cameraClient转换成IBinder类型，将调用的参数写到Parcel（可理解为Binder通信的管道）中，通过BpBinder的transact()函数发送消息，然后由BnCameraService去响应该连接，最后就是等待服务端返回，如果成功则生成一个BpCamera实例。&lt;/p&gt;
&lt;p&gt;真正的服务端响应实现在BnCameraService的onTransact()函数中，其负责解包收到的Parcel并执行client端的请求的方法。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;status_t BnCameraService::onTransact(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    uint32_t code, const Parcel&amp;amp; data, Parcel* reply, uint32_t flags)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    switch(code) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     case CONNECT: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            CHECK_INTERFACE(ICameraService, data, reply);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            sp&amp;lt;ICameraClient&amp;gt; cameraClient =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    interface_cast&amp;lt;ICameraClient&amp;gt;(data.readStrongBinder()); // 使用Camera的Binder对象生成Camera客户代理BpCameraClient实例&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;              int32_t cameraId = data.readInt32();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            const String16 clientName = data.readString16();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            int32_t clientUid = data.readInt32();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            sp&amp;lt;ICamera&amp;gt; camera;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            status_t status = connect(cameraClient, cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    clientName, clientUid, /*out*/camera); // 将生成的BpCameraClient对象作为参数传递到CameraService的connect()函数中&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;              reply-&amp;gt;writeNoException();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            reply-&amp;gt;writeInt32(status); // 将BpCamera对象以IBinder的形式打包到Parcel中返回&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;              if (camera != NULL) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                reply-&amp;gt;writeInt32(1);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                reply-&amp;gt;writeStrongBinder(camera-&amp;gt;asBinder());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                reply-&amp;gt;writeInt32(0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return NO_ERROR;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;主要的处理包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过data中Camera的Binder对象生成Camera客户代理BpCameraClient实例；&lt;/li&gt;
&lt;li&gt;将生成的BpCameraClient对象作为参数传递到CameraService（/frameworks/av/services/camera /libcameraservice/CameraService.cpp）的connect()函数中，该函数会返回一个BpCamera实例；&lt;/li&gt;
&lt;li&gt;将在上述实例对象以IBinder的形式打包到Parcel中返回。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后，BpCamera实例是通过CameraService::connect()函数返回的。CameraService::connect()实现的核心是调用connectHelperLocked()函数根据HAL不同API的版本创建不同的client实例（早期版本中好像没有connectHelperLocked()这个函数，但功能基本相似）。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;70&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;status_t CameraService::connectHelperLocked(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        /*out*/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        sp&amp;lt;Client&amp;gt;&amp;amp; client,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        /*in*/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        const sp&amp;lt;ICameraClient&amp;gt;&amp;amp; cameraClient,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        const String16&amp;amp; clientPackageName,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int clientUid,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int callingPid,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int halVersion,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        bool legacyMode) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int facing = -1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int deviceVersion = getDeviceVersion(cameraId, &amp;amp;facing);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (halVersion &amp;lt; 0 || halVersion == deviceVersion) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Default path: HAL version is unspecified by caller, create CameraClient&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // based on device version reported by the HAL.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        switch(deviceVersion) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_1_0:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            client = new CameraClient(this, cameraClient,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    clientPackageName, cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    facing, callingPid, clientUid, getpid(), legacyMode);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_2_0:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_2_1:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_3_0:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_3_1:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case CAMERA_DEVICE_API_VERSION_3_2:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            client = new Camera2Client(this, cameraClient,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    clientPackageName, cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    facing, callingPid, clientUid, getpid(), legacyMode);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          case -1:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            ALOGE(&amp;quot;Invalid camera id %d&amp;quot;, cameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return BAD_VALUE;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          default:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            ALOGE(&amp;quot;Unknown camera device HAL version: %d&amp;quot;, deviceVersion);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return INVALID_OPERATION;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // A particular HAL version is requested by caller. Create CameraClient&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // based on the requested HAL version.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (deviceVersion &amp;gt; CAMERA_DEVICE_API_VERSION_1_0 &amp;amp;&amp;amp;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            halVersion == CAMERA_DEVICE_API_VERSION_1_0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            // Only support higher HAL version device opened as HAL1.0 device.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            client = new CameraClient(this, cameraClient,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    clientPackageName, cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    facing, callingPid, clientUid, getpid(), legacyMode);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            ALOGE(&amp;quot;Invalid camera HAL version %x: HAL %x device can only be&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &amp;quot; opened as HAL %x device&amp;quot;, halVersion, deviceVersion,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    CAMERA_DEVICE_API_VERSION_1_0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return INVALID_OPERATION;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    status_t status = connectFinishUnsafe(client, client-&amp;gt;getRemote());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (status != OK) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // this is probably not recoverable.. maybe the client can try again&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return status;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mClient[cameraId] = client;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    LOG1(&amp;quot;CameraService::connect X (id %d, this pid is %d)&amp;quot;, cameraId,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;         getpid());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return OK;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;可见，在CAMERA_DEVICE_API_VERSION_2_0之前使用CameraClient进行实例化，之后则采用Camera2Client进行实例化。以CameraClient为例，其initialize()函数如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;status_t CameraClient::initialize(camera_module_t *module) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int callingPid = getCallingPid();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    status_t res;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    LOG1(&amp;quot;CameraClient::initialize E (pid %d, id %d)&amp;quot;, callingPid, mCameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Verify ops permissions&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    res = startCameraOps();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (res != OK) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return res;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    char camera_device_name[10];&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    snprintf(camera_device_name, sizeof(camera_device_name), &amp;quot;%d&amp;quot;, mCameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHardware = new CameraHardwareInterface(camera_device_name);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    res = mHardware-&amp;gt;initialize(&amp;amp;module-&amp;gt;common);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (res != OK) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ALOGE(&amp;quot;%s: Camera %d: unable to initialize device: %s (%d)&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                __FUNCTION__, mCameraId, strerror(-res), res);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mHardware.clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return res;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHardware-&amp;gt;setCallbacks(notifyCallback,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            dataCallback,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            dataCallbackTimestamp,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            (void *)(uintptr_t)mCameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Enable zoom, error, focus, and metadata messages by default&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                  CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    LOG1(&amp;quot;CameraClient::initialize X (pid %d, id %d)&amp;quot;, callingPid, mCameraId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return OK;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;上述函数中，主要注意以下流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;加粗的代码CameraHardwareInterface新建了了一个Camera硬件接口，当然，camera_device_name为摄像头设备名；&lt;/li&gt;
&lt;li&gt;mHardware-&amp;gt;initialize(&amp;amp;module-&amp;gt;common)调用底层硬件的初始化方法；&lt;/li&gt;
&lt;li&gt;mHardware-&amp;gt;setCallbacks将CamerService处的回调函数注册到HAL处。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CameraHardwareInterface定义了Camera的硬件抽象特征，由此进入到HAL。&lt;/p&gt;
&lt;h2 id=&quot;四、HAL：CameraHardwareInterface&quot;&gt;&lt;a href=&quot;#四、HAL：CameraHardwareInterface&quot; class=&quot;headerlink&quot; title=&quot;四、HAL：CameraHardwareInterface&quot;&gt;&lt;/a&gt;四、HAL：CameraHardwareInterface&lt;/h2&gt;&lt;p&gt;CameraHardwareInterface的作用在于链接Camera Server和V4L2，通过实现CameraHardwareInterface可以屏蔽不同的driver对Camera Server的影响。CameraHardwareInterface同样虚拟继承自RefBase。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;class CameraHardwareInterface : public virtual RefBase &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    CameraHardwareInterface(const char *name)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mDevice = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mName = name;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;CameraHardwareInterface中包含了控制通道和数据通道，控制通道用于处理预览和视频获取的开始/停止、拍摄照片、自动对焦等功能，数据通道通过回调函数来获得预览、视频录制、自动对焦等数据。当需要支持新的硬件时就需要继承于CameraHardwareInterface ，来实现对应的功能。CameraHardwareInterface提供的public方法如下：&lt;br&gt;&lt;img src=&quot;/img/20160401-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;在前一节中，initialize()函数调用了mHardware-&amp;gt;initialize和mHardware-&amp;gt;setCallbacks，下面来看下CameraHardwareInterface.h对其的实现。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;status_t initialize(hw_module_t *module)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ALOGI(&amp;quot;Opening camera %s&amp;quot;, mName.string());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    camera_module_t *cameraModule = reinterpret_cast&amp;lt;camera_module_t *&amp;gt;(module);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    camera_info info;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    status_t res = cameraModule-&amp;gt;get_camera_info(atoi(mName.string()), &amp;amp;info);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (res != OK) return res;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int rc = OK;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (module-&amp;gt;module_api_version &amp;gt;= CAMERA_MODULE_API_VERSION_2_3 &amp;amp;&amp;amp;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        info.device_version &amp;gt; CAMERA_DEVICE_API_VERSION_1_0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Open higher version camera device as HAL1.0 device.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        rc = cameraModule-&amp;gt;open_legacy(module, mName.string(),&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                           CAMERA_DEVICE_API_VERSION_1_0,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                           (hw_device_t **)&amp;amp;mDevice);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        rc = CameraService::filterOpenErrorCode(module-&amp;gt;methods-&amp;gt;open(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            module, mName.string(), (hw_device_t **)&amp;amp;mDevice));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (rc != OK) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ALOGE(&amp;quot;Could not open camera %s: %d&amp;quot;, mName.string(), rc);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return rc;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    initHalPreviewWindow();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return rc;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在initialize()方法中，通过cameraModule-&amp;gt;open_legacy打开摄像头模组，initHalPreviewWindow()用于初始化Preview的相关流opspreview_stream_ops，初始化hal的预览窗口。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;void initHalPreviewWindow()&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.lock_buffer = __lock_buffer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_crop = __set_crop;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_timestamp = __set_timestamp;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_usage = __set_usage;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHalPreviewWindow.nw.get_min_undequeued_buffer_count =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            __get_min_undequeued_buffer_count;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** Set the notification and data callbacks */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;void setCallbacks(notify_callback notify_cb,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                  data_callback data_cb,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                  data_callback_timestamp data_cb_timestamp,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                  void* user)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mNotifyCb = notify_cb;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataCb = data_cb;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataCbTimestamp = data_cb_timestamp;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCbUser = user;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ALOGV(&amp;quot;%s(%s)&amp;quot;, __FUNCTION__, mName.string());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mDevice-&amp;gt;ops-&amp;gt;set_callbacks) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mDevice-&amp;gt;ops-&amp;gt;set_callbacks(mDevice,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                               __notify_cb,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                               __data_cb,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                               __data_cb_timestamp,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                               __get_memory,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                               this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;set_callbacks中，&lt;strong&gt;notify_cb、&lt;/strong&gt;data_cb、&lt;strong&gt;data_cb_timestamp和&lt;/strong&gt;get_memory分别消息回调，数据回调，时间戳回调，以及内存相关操作的回调。&lt;/p&gt;
&lt;p&gt;以上通过简略分析应用层调用Camera.open()之后在Framework、ART、Library以及HAL层的响应，来说明Android中Camera系统的整体架构，希望对读者能有一定的帮助，后续将在理解Camera整体架构的基础，探索更加高效的Preview方式，敬请期待！&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;Camera的架构与Android系统的整体架构保持一致，如下图所示，本文主要从以下四个方面对其进行说明。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Framework：Camera.java&lt;/li&gt;
&lt;li&gt;Android Runtime：android_hardware_Camera
    
    </summary>
    
      <category term="Android源码笔记" scheme="http://yhthu.com/categories/Android%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="Android Camera" scheme="http://yhthu.com/tags/Android-Camera/"/>
    
  </entry>
  
  <entry>
    <title>Android线程管理（三）——Thread类的内部原理、休眠及唤醒</title>
    <link href="http://yhthu.com/2016/05/03/201605034/"/>
    <id>http://yhthu.com/2016/05/03/201605034/</id>
    <published>2016-05-03T10:15:54.938Z</published>
    <updated>2016-05-03T10:16:00.537Z</updated>
    
    <content type="html">&lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;《Android线程管理（一）——线程通信》&lt;/li&gt;
&lt;li&gt;《Android线程管理（二）——ActivityThread》&lt;/li&gt;
&lt;li&gt;《Android线程管理（三）——Thread类的内部原理、休眠及唤醒》&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;三、Thread类的内部原理、休眠及唤醒&quot;&gt;&lt;a href=&quot;#三、Thread类的内部原理、休眠及唤醒&quot; class=&quot;headerlink&quot; title=&quot;三、Thread类的内部原理、休眠及唤醒&quot;&gt;&lt;/a&gt;三、Thread类的内部原理、休眠及唤醒&lt;/h2&gt;&lt;h3 id=&quot;3-1-Thread类的内部原理&quot;&gt;&lt;a href=&quot;#3-1-Thread类的内部原理&quot; class=&quot;headerlink&quot; title=&quot;3.1 Thread类的内部原理&quot;&gt;&lt;/a&gt;3.1 Thread类的内部原理&lt;/h3&gt;&lt;p&gt;线程是CPU资源调度的基本单位，属于抽象范畴，Java通过Thread类完成线程管理。Thread类本质其实是“可执行代码”，其实现了Runnable接口，而Runnable接口唯一的方法就是run()。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class Thread implements Runnable &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public interface Runnable &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * Starts executing the active part of the class&amp;apos; code. This method is&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * called when a thread is started that has been created with a class which&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * implements &amp;#123;@code Runnable&amp;#125;.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void run();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt; 从注释可以看出，调用Thread的start()方法就是间接调用Runnable接口的run()方法。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public synchronized void start() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    checkNotStarted();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    hasBeenStarted = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    VMThread.create(this, stackSize);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;start()方法中VMThread.create(this, stackSize)是真正创建CPU线程的地方，换句话说，只有调用start()后的Thread才真正创建CPU线程，而新创建的线程中运行的就是Runnable接口的run()方法。&lt;/p&gt;
&lt;h3 id=&quot;3-2-线程休眠及唤醒&quot;&gt;&lt;a href=&quot;#3-2-线程休眠及唤醒&quot; class=&quot;headerlink&quot; title=&quot;3.2 线程休眠及唤醒&quot;&gt;&lt;/a&gt;3.2 线程休眠及唤醒&lt;/h3&gt;&lt;p&gt;线程通信、同步、协作是多线程编程中常见的问题。线程协作通常是采用线程休眠及唤醒来实现的，线程的休眠通过等待某个对象的锁（monitor）实现（wait()方法），当其他线程调用该对象的notify()方法时，该线程就被唤醒。该对象实现在线程间数据传递，多个线程通过该对象实现协作。&lt;/p&gt;
&lt;p&gt;线程协作的经典例子是Java设计模式中的“生产者-消费者模式”，生产者不断往缓冲区写入数据，消费者从缓冲区中取出数据进行消费。在实现上，生产者与消费者分别继承Thread，缓冲区采用优先级队列PriorityQueue来模拟。生产者将数据放入缓冲区的前提是缓冲区有剩余空间，消费者从缓冲区中取出数据的前提是缓冲区中有数据，因此，这就涉及到生成者线程与消费者线程之间的协作。下面通过代码简要说明下。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;60&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class TestWait &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private int size = 5;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private PriorityQueue&amp;lt;Integer&amp;gt; queue = new PriorityQueue&amp;lt;Integer&amp;gt;(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public static void main(String[] args) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        TestWait test = new TestWait();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Producer producer = test.new Producer();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Consumer consumer = test.new Consumer();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        producer.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        consumer.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    class Consumer extends Thread &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            while (true) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                synchronized (queue) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    while (queue.size() == 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            System.out.println(&amp;quot;队列空，等待数据&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            queue.wait();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &amp;#125; catch (InterruptedException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            queue.notify();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    queue.poll(); // 每次移走队首元素&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                       queue.notify();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    System.out.println(&amp;quot;从队列取走一个元素，队列剩余&amp;quot; + queue.size() + &amp;quot;个元素&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    class Producer extends Thread &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            while (true) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                synchronized (queue) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    while (queue.size() == size) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            System.out.println(&amp;quot;队列满，等待有空余空间&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            queue.wait();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &amp;#125; catch (InterruptedException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            queue.notify();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    queue.offer(1); // 每次插入一个元素&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                       queue.notify();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    System.out.println(&amp;quot;向队列取中插入一个元素，队列剩余空间：&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                            + (size - queue.size()));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;这段代码在很多讲述生产者-消费者模式的地方都会用到，其中，Producer线程首先启动，synchronized关键字使其能够获得queue的锁，其他线程处于等待状态。初始queue为空，通过offer向缓冲区队列写入数据，notify()方法使得等待该缓冲区queue的线程（此处为消费者线程）唤醒，但该线程并不能马上获得queue的锁，只有等生产者线程不断向queue中写入数据直到queue.size() == size，此时缓冲队列充满，生产者线程调用wait()方法进入等待状态。此时，消费者线程处于唤醒并且获得queue的锁，通过poll()方法消费缓冲区中的数据，同理，虽然调用了notify()方法使得生产者线程被唤醒，但其并不能马上获得queue的锁，只有等消费者线程不断消费数据直到queue.size() == 0，消费者线程调用wait()方法进入等待状态，生产者线程重新获得queue的锁，循环上述过程，从而完成生产者线程与消费者线程的协作。&lt;/p&gt;
&lt;p&gt;在Android的SystemServer中有多处用到了线程协作的方式，比如WindowManagerService的main()中通过runWithScissors()启动的BlockingRunnable与SystemServer所在线程的协作。WindowManagerService源码地址可参考：&lt;a href=&quot;https://github.com/android/platform_frameworks_base/blob/master/services/core/java/com/android/server/wm/WindowManagerService.java&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/android/platform_frameworks_base/blob/master/services/core/java/com/android/server/wm/WindowManagerService.java&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-3-线程中断&quot;&gt;&lt;a href=&quot;#3-3-线程中断&quot; class=&quot;headerlink&quot; title=&quot;3.3 线程中断&quot;&gt;&lt;/a&gt;3.3 线程中断&lt;/h3&gt;&lt;p&gt;在Java中“中断”线程是通过interrupt()方法来实现的，之所以加引号，是因为interrupt()并不中断正在运行的线程，只是向线程发送一个中断请求，具体行为依赖于线程的状态，在文档中有如下说明：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Posts an interrupt request to this Thread. The behavior depends on the state of this Thread:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Threads blocked in one of Object&amp;apos;s wait() methods or one of Thread&amp;apos;s join() or sleep() methods will be woken up, their interrupt status will be cleared, and they receive an InterruptedException. &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Threads blocked in an I/O operation of an java.nio.channels.InterruptibleChannel will have their interrupt status set and receive an java.nio.channels.ClosedByInterruptException. Also, the channel will be closed. &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Threads blocked in a java.nio.channels.Selector will have their interrupt status set and return immediately. They don&amp;apos;t receive an exception in this case.&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;翻译下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果线程处于阻塞状态，即线程被Object.wait()、Thread.join()或 Thread.sleep()阻塞，调用interrupt()方法，将接收到InterruptedException异常，中断状态被清除，结束阻塞状态；&lt;/li&gt;
&lt;li&gt;如果线程在进行I/O操作（java.nio.channels.InterruptibleChannel）时被阻塞，那么线程将收到java.nio.channels.ClosedByInterruptException异常，通道被关闭，结束阻塞状态；&lt;/li&gt;
&lt;li&gt;如果线程被阻塞在java.nio.channels.Selector中，那么中断状态会被置位并返回，不会抛出异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void interrupt() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Interrupt this thread before running actions so that other&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // threads that observe the interrupt as a result of an action&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // will see that this thread is in the interrupted state.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    VMThread vmt = this.vmThread;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (vmt != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        vmt.interrupt();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (interruptActions) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        for (int i = interruptActions.size() - 1; i &amp;gt;= 0; i--) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            interruptActions.get(i).run();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&quot;3-4-join-和sleep-方法&quot;&gt;&lt;a href=&quot;#3-4-join-和sleep-方法&quot; class=&quot;headerlink&quot; title=&quot;3.4 join()和sleep()方法&quot;&gt;&lt;/a&gt;3.4 join()和sleep()方法&lt;/h3&gt;&lt;p&gt;join()方法也可以理解为线程之间协作的一种方式，当两个线程需要顺序执行时，调用第一个线程的join()方法能使该线程阻塞，其依然通过wait()方法来实现的。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Blocks the current Thread (&amp;lt;code&amp;gt;Thread.currentThread()&amp;lt;/code&amp;gt;) until&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * the receiver finishes its execution and dies.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @throws InterruptedException if &amp;lt;code&amp;gt;interrupt()&amp;lt;/code&amp;gt; was called for&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *         the receiver while it was in the &amp;lt;code&amp;gt;join()&amp;lt;/code&amp;gt; call&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @see Object#notifyAll&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @see java.lang.ThreadDeath&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public final void join() throws InterruptedException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    VMThread t = vmThread;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (t == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (t) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        while (isAlive()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            t.wait();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;另外，还有带时间参数的join()方法，在超出规定时间后，退出阻塞状态。同样的，其通过带时间参数的wait()方法实现而已。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public final void join(long millis) throws InterruptedException&amp;#123;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public final void join(long millis, int nanos) throws InterruptedException &amp;#123;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;sleep()与wait()的相同之处在于它们都是通过等待阻塞线程，不同之处在于sleep()等待的是时间，wait()等待的是对象的锁。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static void sleep(long time) throws InterruptedException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Thread.sleep(time, 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static void sleep(long millis, int nanos) throws InterruptedException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    VMThread.sleep(millis, nanos);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&quot;3-5-CountDownLatch&quot;&gt;&lt;a href=&quot;#3-5-CountDownLatch&quot; class=&quot;headerlink&quot; title=&quot;3.5 CountDownLatch&quot;&gt;&lt;/a&gt;3.5 CountDownLatch&lt;/h3&gt;&lt;p&gt;CountDownLatch位于java.util.concurrent.CountDownLatch，实现倒数计数锁存器，当计数减至0时，触发特定的事件。在某些主线程需要等到子线程的应用很实用，以Google的zxing开源库中的一段代码为例进行说明：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;final class DecodeThread extends Thread &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  private final CountDownLatch handlerInitLatch;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  DecodeThread(CaptureActivity activity,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;               Collection&amp;lt;BarcodeFormat&amp;gt; decodeFormats,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;               Map&amp;lt;DecodeHintType,?&amp;gt; baseHints,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;               String characterSet,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;               ResultPointCallback resultPointCallback) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    this.activity = activity;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    handlerInitLatch = new CountDownLatch(1);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  Handler getHandler() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;      handlerInitLatch.await();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (InterruptedException ie) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;      // continue?&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return handler;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Looper.prepare();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    handler = new DecodeHandler(activity, hints);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    handlerInitLatch.countDown();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Looper.loop();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在上述例子中，首先在DecodeThread构造器中初始化CountDownLatch对象，并传入初始化参数1。其次，在run()方法中调用CountDownLatch对象的countDown()方法，这很好的保证了外部实例通过getHandler()方法获取handler时，handler不为null。&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li
    
    </summary>
    
      <category term="Android线程管理" scheme="http://yhthu.com/categories/Android%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86/"/>
    
    
      <category term="Android线程" scheme="http://yhthu.com/tags/Android%E7%BA%BF%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Android线程管理（二）——ActivityThread</title>
    <link href="http://yhthu.com/2016/05/03/201605033/"/>
    <id>http://yhthu.com/2016/05/03/201605033/</id>
    <published>2016-05-03T10:10:30.030Z</published>
    <updated>2016-05-03T10:10:34.487Z</updated>
    
    <content type="html">&lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;《Android线程管理（一）——线程通信》&lt;/li&gt;
&lt;li&gt;《Android线程管理（二）——ActivityThread》&lt;/li&gt;
&lt;li&gt;《Android线程管理（三）——Thread类的内部原理、休眠及唤醒》&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;二、ActivityThread的主要工作及实现机制&quot;&gt;&lt;a href=&quot;#二、ActivityThread的主要工作及实现机制&quot; class=&quot;headerlink&quot; title=&quot;二、ActivityThread的主要工作及实现机制&quot;&gt;&lt;/a&gt;二、ActivityThread的主要工作及实现机制&lt;/h2&gt;&lt;p&gt;ActivityThread是Android应用的主线程（UI线程），说起ActivityThread，不得不提到Activity的创建、启动过程以及ActivityManagerService，但本文将仅从线程管理的角度来分析ActivityThread。ActivityManagerService、ActivityStack、ApplicationThread等会在后续文章中详细分析，敬请期待喔~~不过为了说清楚ActivityThread的由来，还是需要简单介绍下。&lt;/p&gt;
&lt;p&gt;以下引用自罗升阳大师的博客：《Android应用程序的Activity启动过程简要介绍和学习计划》&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Step 1. 无论是通过Launcher来启动Activity，还是通过Activity内部调用startActivity接口来启动新的Activity，都通过Binder进程间通信进入到ActivityManagerService进程中，并且调用ActivityManagerService.startActivity接口；&lt;br&gt;Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息；&lt;br&gt;Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了，这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程，对于通过点击应用程序图标的情景来说，这个进程就是Launcher了，而对于通过在Activity内部调用startActivity的情景来说，这个进程就是这个Activity所在的进程了；&lt;br&gt;Step 4. ApplicationThread不执行真正的启动操作，它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中，看看是否需要创建新的进程来启动Activity；&lt;br&gt;Step 5. 对于通过点击应用程序图标来启动Activity的情景来说，ActivityManagerService在这一步中，会调用startProcessLocked来创建一个新的进程，而对于通过在Activity内部调用startActivity来启动新的Activity来说，这一步是不需要执行的，因为新的Activity就在原来的Activity所在的进程中进行启动；&lt;br&gt;Step 6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口，通知相应的进程执行启动Activity的操作；&lt;br&gt;Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread，ActivityThread通过ClassLoader导入相应的Activity类，然后把它启动起来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;大师的这段描述把ActivityManagerService、ActivityStack、ApplicationThread及ActivityThread的调用关系讲的很清楚，本文将从ActivityThread的main()方法开始分析其主要工作及实现机制。&lt;/p&gt;
&lt;p&gt;ActivityThread源码来自：&lt;a href=&quot;https://github.com/android/platform_frameworks_base/blob/master/core/java/android/app/ActivityThread.java&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/android/platform_frameworks_base/blob/master/core/java/android/app/ActivityThread.java&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static void main(String[] args) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, &amp;quot;ActivityThreadMain&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    SamplingProfilerIntegration.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // CloseGuard defaults to true and can be quite spammy.  We&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // disable it here, but selectively enable it later (via&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // StrictMode) on debug builds, but using DropBox, not logs.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    CloseGuard.setEnabled(false);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Environment.initForCurrentUser();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Set the reporter for event logging in libcore&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    EventLogger.setReporter(new EventLoggingReporter());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    AndroidKeyStoreProvider.install();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Make sure TrustedCertificateStore looks in the right place for CA certificates&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    TrustedCertificateStore.setDefaultUserDirectory(configDir);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Process.setArgV0(&amp;quot;&amp;lt;pre-initialized&amp;gt;&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Looper.prepareMainLooper();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ActivityThread thread = new ActivityThread();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    thread.attach(false);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (sMainThreadHandler == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        sMainThreadHandler = thread.getHandler();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (false) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Looper.myLooper().setMessageLogging(new&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                LogPrinter(Log.DEBUG, &amp;quot;ActivityThread&amp;quot;));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // End of event ActivityThreadMain.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Looper.loop();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    throw new RuntimeException(&amp;quot;Main thread loop unexpectedly exited&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;上述代码中，红色部分之前的代码主要用于环境初始化、AndroidKeyStoreProvider安装等，这里不做重点说明。红色部分的代码主要分为两个功能块：1）绑定应用进程到ActivityManagerService；2）主线程Handler消息处理。&lt;/p&gt;
&lt;p&gt;关于线程通信机制，Handler、MessageQueue、Message及Looper四者的关系请参考上一篇文章《Android线程管理——线程通信》。&lt;/p&gt;
&lt;h3 id=&quot;2-1-应用进程绑定&quot;&gt;&lt;a href=&quot;#2-1-应用进程绑定&quot; class=&quot;headerlink&quot; title=&quot;2.1 应用进程绑定&quot;&gt;&lt;/a&gt;2.1 应用进程绑定&lt;/h3&gt;&lt;p&gt;main()方法通过thread.attach(false)绑定应用进程。ActivityManagerNative通过getDefault()方法返回ActivityManagerService实例，ActivityManagerService通过attachApplication将ApplicationThread对象绑定到ActivityManagerService，而ApplicationThread作为Binder实现ActivityManagerService对应用进程的通信和控制。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private void attach(boolean system) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        sCurrentActivityThread = this;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mSystemThread = system;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (!system) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            ……            RuntimeInit.setApplicationObject(mAppThread.asBinder());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            final IActivityManager mgr = ActivityManagerNative.getDefault();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                mgr.attachApplication(mAppThread);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125; catch (RemoteException ex) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                // Ignore&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            ……        &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在ActivityManagerService内部，attachApplication实际是通过调用attachApplicationLocked实现的，这里采用了synchronized关键字保证同步。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public final void attachApplication(IApplicationThread thread) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (this) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int callingPid = Binder.getCallingPid();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        final long origId = Binder.clearCallingIdentity();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        attachApplicationLocked(thread, callingPid);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Binder.restoreCallingIdentity(origId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;attachApplicationLocked的实现较为复杂，其主要功能分为两部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;thread.bindApplication&lt;/li&gt;
&lt;li&gt;mStackSupervisor.attachApplicationLocked(app)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private final boolean attachApplicationLocked(IApplicationThread thread,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            int pid) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Find the application record that is being attached...  either via&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // the pid if we are running in multiple processes, or just pull the&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // next app record if we are emulating process with anonymous threads.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ProcessRecord app;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (pid != MY_PID &amp;amp;&amp;amp; pid &amp;gt;= 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        synchronized (mPidsSelfLocked) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            app = mPidsSelfLocked.get(pid);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        app = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;   // ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       // ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                new Configuration(mConfiguration), app.compat,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                getCommonServicesLocked(app.isolated),&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                mCoreSettingsObserver.getCoreSettingsLocked());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        updateLruProcessLocked(app, false, null);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // todo: Yikes!  What should we do?  For now we will try to&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // start another process, but that could easily get us in&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // an infinite loop of restarting processes...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Slog.wtf(TAG, &amp;quot;Exception thrown during bind of &amp;quot; + app, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        app.resetPackageList(mProcessStats);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        app.unlinkDeathRecipient();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        startProcessLocked(app, &amp;quot;bind fail&amp;quot;, processName);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // See if the top visible activity is waiting to run in this process...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (normalMode) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (mStackSupervisor.attachApplicationLocked(app)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                didSomething = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; catch (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            Slog.wtf(TAG, &amp;quot;Exception thrown launching activities in &amp;quot; + app, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            badApp = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;thread对象其实是ActivityThread里ApplicationThread对象在ActivityManagerService的代理对象，故此执行thread.bindApplication，最终会调用ApplicationThread的bindApplication方法。该bindApplication方法的实质是通过向ActivityThread的消息队列发送BIND_APPLICATION消息，消息的处理调用handleBindApplication方法，handleBindApplication方法比较重要的是会调用如下方法：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;mInstrumentation.callApplicationOnCreate(app);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;callApplicationOnCreate即调用应用程序Application的onCreate()方法，说明Application的onCreate()方法会比所有activity的onCreate()方法先调用。&lt;/p&gt;
&lt;p&gt;mStackSupervisor为ActivityManagerService的成员变量，类型为ActivityStackSupervisor。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** Run all ActivityStacks through this */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;ActivityStackSupervisor mStackSupervisor;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;从注释可以看出，mStackSupervisor为Activity堆栈管理辅助类实例。ActivityStackSupervisor的attachApplicationLocked()方法的调用了realStartActivityLocked()方法，在realStartActivityLocked()方法中，会调用scheduleLaunchActivity()方法：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;final boolean realStartActivityLocked(ActivityRecord r,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ProcessRecord app, boolean andResume, boolean checkConfig)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        throws RemoteException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    //...  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        //...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                System.identityHashCode(r), r.info,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                new Configuration(mService.mConfiguration),&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                r.compat, r.icicle, results, newIntents, !andResume,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                mService.isNextTransitionForward(), profileFile, profileFd,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                profileAutoStop);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        //...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (RemoteException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        //...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    //...    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;app.thread也是ApplicationThread对象在ActivityManagerService的一个代理对象，最终会调用ApplicationThread的scheduleLaunchActivity方法。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// we use token to identify this activity without having to send the&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// activity itself back to the activity manager. (matters more with ipc)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ActivityInfo info, Configuration curConfig, Configuration overrideConfig,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int procState, Bundle state, PersistableBundle persistentState,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    List&amp;lt;ResultInfo&amp;gt; pendingResults, List&amp;lt;ReferrerIntent&amp;gt; pendingNewIntents,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        updateProcessState(procState, false);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ActivityClientRecord r = new ActivityClientRecord();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        sendMessage(H.LAUNCH_ACTIVITY, r);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;同bindApplication()方法，最终是通过向ActivityThread的消息队列发送消息，在ActivityThread完成实际的LAUNCH_ACTIVITY的操作。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (DEBUG_MESSAGES) Slog.v(TAG, &amp;quot;&amp;gt;&amp;gt;&amp;gt; handling: &amp;quot; + codeToString(msg.what));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    switch (msg.what) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        case LAUNCH_ACTIVITY: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, &amp;quot;activityStart&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            r.packageInfo = getPackageInfoNoCheck(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                r.activityInfo.applicationInfo, r.compatInfo);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            handleLaunchActivity(r, null);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125; break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;handleLaunchActivity()用于启动Activity。具体的启动流程不在这里详述了，这里重点说明ApplicationThread及ActivityThread的线程通信机制。&lt;/p&gt;
&lt;h3 id=&quot;2-2-主线程消息处理&quot;&gt;&lt;a href=&quot;#2-2-主线程消息处理&quot; class=&quot;headerlink&quot; title=&quot;2.2 主线程消息处理&quot;&gt;&lt;/a&gt;2.2 主线程消息处理&lt;/h3&gt;&lt;p&gt; 在《Android线程管理——线程通信》中谈到了普通线程中Handler、MessageQueue、Message及Looper四者的关系，那么，ActivityThread中的线程通信又有什么不同呢？不同之处主要表现为两点：1）Looper的初始化方式；2）Handler生成。&lt;/p&gt;
&lt;p&gt;首先，ActivityThread通过Looper.prepareMainLooper()初始化Looper，为了直观比较ActivityThread与普通线程初始化Looper的区别，把两种初始化方法放在一起：&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** Initialize the current thread as a looper.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  * This gives you a chance to create handlers that then reference&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  * this looper, before actually starting the loop. Be sure to call&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  * &amp;#123;@link #loop()&amp;#125; after calling this method, and end it by calling&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  * &amp;#123;@link #quit()&amp;#125;.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static void prepare() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    prepare(true);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private static void prepare(boolean quitAllowed) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (sThreadLocal.get() != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        throw new RuntimeException(&amp;quot;Only one Looper may be created per thread&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sThreadLocal.set(new Looper(quitAllowed));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Initialize the current thread as a looper, marking it as an&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * application&amp;apos;s main looper. The main looper for your application&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * is created by the Android environment, so you should never need&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * to call this function yourself.  See also: &amp;#123;@link #prepare()&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static void prepareMainLooper() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    prepare(false);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (Looper.class) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (sMainLooper != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            throw new IllegalStateException(&amp;quot;The main Looper has already been prepared.&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        sMainLooper = myLooper();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;普通线程的prepare()方法默认quitAllowed参数为true，表示允许退出，ActivityThread在prepareMainLooper()方法中调用prepare()方法，参数为false，表示主线程不允许退出。&lt;/li&gt;
&lt;li&gt;普通线程只调用prepare()方法，ActivityThread在调用完prepare()方法之后，会通过myLooper()方法将本地线程&lt;threadlocal&gt;的Looper对象的引用交给sMainLooper。myLooper()其实就是调用sThreadLocal的get()方法实现的。&lt;/threadlocal&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Return the Looper object associated with the current thread.  Returns&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * null if the calling thread is not associated with a Looper.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static Looper myLooper() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return sThreadLocal.get();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;之所以要通过sMainLooper指向ActivityThread的Looper对象，就是希望通过getMainLooper()方法将主线程的Looper对象开放给其他线程。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** Returns the application&amp;apos;s main looper, which lives in the main thread of the application.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static Looper getMainLooper() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (Looper.class) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return sMainLooper;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;其次，ActivityThread与普通线程的Handler生成方式也不一样。普通线程生成一个与Looper绑定的Handler即可，ActivityThread通过sMainThreadHandler指向getHandler()的返回值，而getHandler()方法返回的其实是一个继承Handler的H对象。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private class H extends Handler &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;final H mH = new H();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;final Handler getHandler() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return mH;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;真正实现消息机制“通”信的其实是Looper的loop()方法，loop()方法的核心实现如下：&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Run the message queue in this thread. Be sure to call&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;#123;@link #quit()&amp;#125; to end the loop.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static void loop() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final Looper me = myLooper();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (me == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        throw new RuntimeException(&amp;quot;No Looper; Looper.prepare() wasn&amp;apos;t called on this thread.&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final MessageQueue queue = me.mQueue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Make sure the identity of this thread is that of the local process,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // and keep track of what that identity token actually is.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Binder.clearCallingIdentity();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final long ident = Binder.clearCallingIdentity();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    for (;;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Message msg = queue.next(); // might block&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (msg == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            // No message indicates that the message queue is quitting.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // This must be in a local variable, in case a UI event sets the logger&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Printer logging = me.mLogging;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (logging != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            logging.println(&amp;quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Dispatching to &amp;quot; + msg.target + &amp;quot; &amp;quot; +&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    msg.callback + &amp;quot;: &amp;quot; + msg.what);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        msg.target.dispatchMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (logging != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            logging.println(&amp;quot;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; Finished to &amp;quot; + msg.target + &amp;quot; &amp;quot; + msg.callback);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Make sure that during the course of dispatching the&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // identity of the thread wasn&amp;apos;t corrupted.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        final long newIdent = Binder.clearCallingIdentity();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (ident != newIdent) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            Log.wtf(TAG, &amp;quot;Thread identity changed from 0x&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    + Long.toHexString(ident) + &amp;quot; to 0x&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    + Long.toHexString(newIdent) + &amp;quot; while dispatching to &amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    + msg.target.getClass().getName() + &amp;quot; &amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    + msg.callback + &amp;quot; what=&amp;quot; + msg.what);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        msg.recycle();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;大致流程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先通过上述myLooper()方法获取Looper对象，取出Looper持有的MessageQueue；&lt;/li&gt;
&lt;li&gt;然后从MessageQueue取出Message，如果Message为null，说明线程正在退出；&lt;/li&gt;
&lt;li&gt;Message不为空，则调用Message的target handler对该Message进行分发，具体分发、处理流程可参考《Android线程管理——线程通信》；&lt;/li&gt;
&lt;li&gt;消息处理完毕，调用recycle()方法进行回收。&lt;/li&gt;
&lt;/ul&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li
    
    </summary>
    
      <category term="Android线程管理" scheme="http://yhthu.com/categories/Android%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86/"/>
    
    
      <category term="Android线程" scheme="http://yhthu.com/tags/Android%E7%BA%BF%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Android开发笔记——ListView模块、缓存及性能</title>
    <link href="http://yhthu.com/2016/05/03/201605038/"/>
    <id>http://yhthu.com/2016/05/03/201605038/</id>
    <published>2016-05-03T10:01:08.855Z</published>
    <updated>2016-05-03T10:01:13.228Z</updated>
    
    <content type="html">&lt;p&gt;ListView是Android开发中最常用的组件之一。本文将重点说明如何正确使用ListView，以及使用过程中可能遇到的问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ListView开发模块&lt;/li&gt;
&lt;li&gt;图片缓存&lt;/li&gt;
&lt;li&gt;可能遇到的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、ListView开发模块&quot;&gt;&lt;a href=&quot;#一、ListView开发模块&quot; class=&quot;headerlink&quot; title=&quot;一、ListView开发模块&quot;&gt;&lt;/a&gt;一、ListView开发模块&lt;/h2&gt;&lt;p&gt;从项目实践的角度来看，ListView适合“自底向上”的开发模式，即从每个条目的显示组件，到对其进行控制的数据结构，最后通过Activity等进行使用。主要包括以下模块：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先是item组件，即用于每项布局输出的xml文件。Android SDK中有simple_list_item_1、simple_list_item_2可用，当需要比较丰富的显示效果时，一般通过自定义xml实现。本文以论坛的格式进行说明，主要包括发帖人头像、用户名，帖子的标题、内容、最后回复时间、编辑、收藏、回复等内容，布局文件比较简单，这里截取其中一项显示，用以说明：&lt;br&gt;&lt;img src=&quot;/img/20151023-1.jpg&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;其次是父对象layout文件，即用于Activity或者Fragment的布局输出文件，一般在此输出文件中包含ListView。当然，如果采用ListFragment或ListActivity，并不需要再显示的定义ListView组件。本文中采用Fragment默认的输出文件，当然，也可以采用自定义的布局文件。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;ListView&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:id=&amp;quot;@+id/topic_list&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:layout_width=&amp;quot;fill_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:layout_height=&amp;quot;fill_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:cacheColorHint=&amp;quot;@android:color/transparent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:divider=&amp;quot;@color/topic_divider_color&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:dividerHeight=&amp;quot;1px&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:listSelector=&amp;quot;@android:color/transparent&amp;quot; &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;/ListView&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;定义数据结构（容器），即用于持有单个Item的数据，可以是简单的String，也可以通过抽象Items所需字段组成一个类，抽象的原则是与Item中的组件对应。本文中上图涉及多个字段，因此通过抽象组件形成BBSTopicItem类。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;列表适配器。决定每行Item中具体显示什么内容，以怎样的样式显示等，通常通过继承ArrayAdapter、SimpleAdapter等实现。本文定义BBSTopicAdapter，继承于ArrayAdapter&lt;bbstopicitem&gt;。&lt;/bbstopicitem&gt;&lt;/li&gt;
&lt;li&gt;最后，需要定义一个Activity或Fragment来使用上述模块。需要说明的是，ListView可以直接被ListActivity或者ListFragment使用。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上五个模块就是使用ListView的&lt;strong&gt;基本逻辑框架&lt;/strong&gt;，开发过程中，需要时刻理清它们之间的关系。 &lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;二、图片缓存及相关&quot;&gt;&lt;a href=&quot;#二、图片缓存及相关&quot; class=&quot;headerlink&quot; title=&quot;二、图片缓存及相关&quot;&gt;&lt;/a&gt;二、图片缓存及相关&lt;/h2&gt;&lt;p&gt;在ListView中显示图片是比较常见的应用场景，但加载图片一般需要通过缓存来进行处理。由于虚拟机的heapsize默认为16M：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;AndroidRuntime.cpp&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int AndroidRuntime::startVM(JavaVM** pJavaVM, JNIEnv** pEnv) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    property_get(&amp;quot;dalvik.vm.heapsize&amp;quot;, heapsizeOptsBuf+4, &amp;quot;16M&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;（厂商一般会修改为32M，后面会说到这个数值）&lt;br&gt;在操作大尺寸图片时无法分配所需内存，就会引起OOM。因此，使用LruCache来缓存图片是常见的做法。但在其使用过程中，也需要注意一些问题，比如使用线程池下载图片，使用SD卡缓存，ListView滑动流畅性，图片显示错乱等，下面对这些问题进行说明。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 首先是LruCache，该类在android-support-v4的包中提供&lt;/strong&gt;&lt;br&gt;主要算法原理是把最近使用的对象用强引用存储在LinkedHashMap中，并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。&lt;/p&gt;
&lt;p&gt;以前经常会使用一种非常流行的内存缓存技术的实现，即软引用或弱引用 (SoftReference or WeakReference)。但是现在已经不再推荐使用这种方式了，因为从 Android 2.3 (API Level 9)开始，垃圾回收器会更倾向于回收持有软引用或弱引用的对象，这让软引用和弱引用变得不再可靠。另外，Android 3.0 (API Level 11)中，图片的数据会存储在本地的内存当中，因而无法用一种可预见的方式将其释放，这就有潜在的风险造成应用程序的内存溢出并崩溃。&lt;/p&gt;
&lt;p&gt;那么，怎样确定一个合适的缓存大小给LruCache呢？有以下多个因素应该放入考虑范围内，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设备可以为每个应用程序分配多大的内存？&lt;/li&gt;
&lt;li&gt;设备屏幕上一次最多能显示多少张图片？有多少图片需要进行预加载？&lt;/li&gt;
&lt;li&gt;设备的屏幕大小和分辨率分别是多少？一个超高分辨率的设备（例如 Galaxy Nexus)比起一个较低分辨率的设备（例如 Nexus S），在持有相同数量图片的时候，需要更大的缓存空间。&lt;/li&gt;
&lt;li&gt;图片的尺寸和大小，还有每张图片会占据多少内存空间？&lt;/li&gt;
&lt;li&gt;图片被访问的频率有多高？是否有一些图片的访问频率比其它图片要高？如果有的话，应该让一些图片常驻在内存当中，或者使用多个LruCache 对象来区分不同组的图片。&lt;/li&gt;
&lt;li&gt;是否能维持好数量和质量之间的平衡吗？有些时候，存储多个低像素的图片，而在后台去开线程加载高像素的图片会更加的有效。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;并没有一个指定的缓存大小可以满足所有的应用程序&lt;/strong&gt;，通常需要分析程序内存的使用情况，然后制定出一个合适的解决方案。缓存太小，有可能造成图片频繁地被释放和重新加载；而缓存太大，则有可能还是会引起 java.lang.OutOfMemory 异常。&lt;br&gt;不过，读者可能会在不同的地方遇到类似下面这段定义LruCache的代码：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public ImageDownLoader(Context context)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int maxMemory = (int) Runtime.getRuntime().maxMemory();  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int mCacheSize = maxMemory / 8;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMemoryCache = new LruCache&amp;lt;String, Bitmap&amp;gt;(mCacheSize)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        protected int sizeOf(String key, Bitmap value) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            return value.getRowBytes() * value.getHeight();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 使用线程池管理图片下载任务&lt;/strong&gt;&lt;br&gt;线程池自然是为了限制系统中执行线程的数量，通常的一种比较低效的做法是为每一张图片下载开启一个新线程（new thread），线程的创建和销毁将造成极大的性能损耗，而对服务器来讲，维护过多的线程将造成内存消耗过大。总的来讲，使用线程池是执行此类任务的一个基本做法。Java中线程池的顶级接口是Executor，但是严格意义上讲Executor并不是一个线程池，而只是一个执行线程的工具，真正的线程池接口是ExecutorService。配置线程池是略显复杂，尤其是对于线程池的原理不是很清楚的情况下，很有可能配置的线程池不是最优的。在Executors类里提供了一些静态工厂，生成一些常用的线程池。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;newSingleThreadExecutor创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。&lt;/li&gt;
&lt;li&gt;newFixedThreadPool 创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。&lt;/li&gt;
&lt;li&gt;newCachedThreadPool 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，&lt;br&gt;那么就会回收部分空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。&lt;/li&gt;
&lt;li&gt;newScheduledThreadPool创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过下面的代码创建固定大小的线程池：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private ExecutorService getThreadPools()&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if(mImageThreadPool == null)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        synchronized(ExecutorService.class)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if(mImageThreadPool == null)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                mImageThreadPool = Executors.newFixedThreadPool(4);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return mImageThreadPool;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;(本段代码来自互联网)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;对于固定大小的线程池，关键是需要根据实际应用场景设置线程数量，既快速有效的执行下载任务，又不造成资源浪费。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. SD卡存储配合LruCache&lt;/strong&gt;&lt;br&gt;使用SD卡存储下载的图片有多方面的好处，提高图片加载速度（从本地加载肯定比网络要快）、节约用户流量等，因此，除了LruCache，一般还会将图片存储在本地SD卡。因此，加载图片的顺序应该是&lt;/p&gt;
&lt;p&gt;　　a. 首先从LruCache中获取图片；&lt;br&gt;　　b. 如果a的返回值为null，则检查SD卡是否存在图片；&lt;br&gt;　　c. 如果a、b的返回值都为null，则通过网络进行下载。&lt;/p&gt;
&lt;p&gt;　　用代码表述上述逻辑为：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Bitmap bitmap;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if (getBitmapFromMemCache(url) != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    bitmap = getBitmapFromMemCache(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; else if (fileUtils.isFileExists(url) &amp;amp;&amp;amp; fileUtils.getFileSize(url) != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    bitmap = fileUtils.getBitmapFromSD(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    bitmap = getBitmapFormUrl(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;return bitmap;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Tip：如果通过HttpURLConnection下载图片，需要注意一个小问题，如果设置HttpURLConnection对象的DoOutput属性为true（con.setDoOutput(true)），在Android4.0以后，会解析为post请求，导致filenotfound异常（获取图片应该是get请求）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. ListView滑动停止下载&lt;/strong&gt;&lt;br&gt;滑动时停止下载也是提高用户体验的方式之一，因为如果在ListView滑动过程中执行下载任务，将会使得ListView出现卡顿。监听滑动状态改变的方法是&lt;strong&gt;onScrollStateChanged(AbsListView view, int scrollState)&lt;/strong&gt;，该方法在&lt;strong&gt;OnScrollListener&lt;/strong&gt;接口中定义的，而OnScrollListener是&lt;strong&gt;AbsListView&lt;/strong&gt;中为了在列表或网格滚动时执行回调函数而定义的接口。（强烈建议做ListView相关应用的读者熟悉一下AbsListView的源码）&lt;/p&gt;
&lt;p&gt;为了实现下载任务与滑动状态的关联，在自定义列表适配器中实现了OnScrollListener接口，在onScrollStateChanged方法中根据scrollState执行相应的下载任务操作。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void onScrollStateChanged(AbsListView view, int scrollState) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    this.scrollState = scrollState;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        showImage(mFirstVisibleItem, mVisibleItemCount);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        cancelTask();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;（本段代码来自互联网）&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 图片显示错乱&lt;/strong&gt;&lt;br&gt;这是一个比较老生常谈的问题，在百度搜索一下“listview图片错位”会见到一大片帖子在讨论这个问题，这里不再赘述，推荐几个比较靠谱链接：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/lesliefang/p/3619223.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.cnblogs.com/lesliefang/p/3619223.html&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;http://www.trinea.cn/android/android-listview-display-error-image-when-scroll/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.trinea.cn/android/android-listview-display-error-image-when-scroll/&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;http://blog.csdn.net/shineflowers/article/details/41744477&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://blog.csdn.net/shineflowers/article/details/41744477&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;总的来讲，图片缓存是Android开发中比较有意思的一个话题，常用的图片缓存开源库有ImageLoader、Picasso、Glide等，最近由Facebook开源了Fresco（&lt;a href=&quot;http://www.fresco-cn.org/）&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.fresco-cn.org/）&lt;/a&gt; ，根据介绍，它能够从网络、本地存储和本地资源中加载图片。同时，为了节省数据和CPU，它拥有三级缓存。此外，Fresco在显示方面是用了Drawees，可以显示占位符，直到图片加载完成。而当图片从屏幕上消失时，会自动释放图片所占的内存。这里推荐一个关于Android三大图片缓存原理、特性对比的链接：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.csdn.net/article/2015-10-21/2825984#rd&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.csdn.net/article/2015-10-21/2825984#rd&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;三、可能遇到的问题&quot;&gt;&lt;a href=&quot;#三、可能遇到的问题&quot; class=&quot;headerlink&quot; title=&quot;三、可能遇到的问题&quot;&gt;&lt;/a&gt;三、可能遇到的问题&lt;/h2&gt;&lt;p&gt; &lt;strong&gt;1. notifyDataSetChanged与局部更新&lt;/strong&gt;&lt;br&gt; 首先举一个栗子：QQ空间或者朋友圈的点赞功能，点赞之后页面会马上刷新，但不会影响本条目以外的其他条目的显示。换句话说，它使用了局部更新，而非notifyDataSetChanged。在了解notifyDataSetChanged与局部更新区别时，需要先对以下问题作出解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;notifyDataSetChanged如何刷新界面？&lt;/li&gt;
&lt;li&gt;什么场景需要使用notifyDataSetChanged？什么场景需要使用局部更新？&lt;/li&gt;
&lt;li&gt;局部更新如何实现？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;回到代码中，notifyDataSetChanged是在BaseAdapter中定义的，首先初始了一个DataSetObservable类的final实例mDataSetObservable：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private final DataSetObservable mDataSetObservable = new DataSetObservable();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;notifyDataSetChanged就是通过操作mDataSetObservable实现的，DataSetObservable是观察者模式的一个实现（Android源码中有很多类似设计模式的实现）。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void registerDataSetObserver(DataSetObserver observer) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataSetObservable.registerObserver(observer);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void unregisterDataSetObserver(DataSetObserver observer) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataSetObservable.unregisterObserver(observer);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void notifyDataSetChanged() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataSetObservable.notifyChanged();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void notifyDataSetInvalidated() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataSetObservable.notifyInvalidated();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;notifyDataSetChanged调用了notifyChanged方法，回到DataSetObservable中：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void notifyChanged() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized(mObservers) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // since onChanged() is implemented by the app, it could do anything, including&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // removing itself from &amp;#123;@link mObservers&amp;#125; - and that could cause problems if&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // an iterator is used on the ArrayList &amp;#123;@link mObservers&amp;#125;.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // to avoid such problems, just march thru the list in the reverse order.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        for (int i = mObservers.size() - 1; i &amp;gt;= 0; i--) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            mObservers.get(i).onChanged();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void notifyInvalidated() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    synchronized (mObservers) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        for (int i = mObservers.size() - 1; i &amp;gt;= 0; i--) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            mObservers.get(i).onInvalidated();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;notifyChanged也只是调用了其绑定的接口，并没有具体的实现，那么这个接口是什么时候绑定的呢？回忆ListView与adapter的关联是何时开始的呢？setAdapter！是的，从setAdapter的代码中可以看到这种关联。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;54&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void setAdapter(ListAdapter adapter) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mAdapter != null &amp;amp;&amp;amp; mDataSetObserver != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAdapter.unregisterDataSetObserver(mDataSetObserver);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    resetList();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mRecycler.clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mHeaderViewInfos.size() &amp;gt; 0|| mFooterViewInfos.size() &amp;gt; 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAdapter = adapter;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mOldSelectedPosition = INVALID_POSITION;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mOldSelectedRowId = INVALID_ROW_ID;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // AbsListView#setAdapter will update choice mode states.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super.setAdapter(adapter);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mAdapter != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mOldItemCount = mItemCount;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mItemCount = mAdapter.getCount();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        checkFocus();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 原来是在这里绑定了数据改变的观察者对象&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mDataSetObserver = new AdapterDataSetObserver();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAdapter.registerDataSetObserver(mDataSetObserver);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int position;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (mStackFromBottom) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            position = lookForSelectablePosition(mItemCount - 1, false);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            position = lookForSelectablePosition(0, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        setSelectedPositionInt(position);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        setNextSelectedPositionInt(position);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (mItemCount == 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            // Nothing selected&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            checkSelectionChanged();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAreAllItemsSelectable = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        checkFocus();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Nothing selected&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        checkSelectionChanged();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    requestLayout();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;前面提到，观察者对象调用的onChanged方法，可以确定，上述绑定的AdapterDataSetObserver中必然有onChanged方法的实现。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void onChanged() &amp;#123; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mDataChanged = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mOldItemCount = mItemCount;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mItemCount = getAdapter().getCount();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if ((getAdapter().hasStableIds()) &amp;amp;&amp;amp; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        (mInstanceState != null) &amp;amp;&amp;amp; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        (mOldItemCount == 0) &amp;amp;&amp;amp; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        (mItemCount &amp;gt; 0)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        onRestoreInstanceState(mInstanceState);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mInstanceState = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        rememberSyncState();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    checkFocus();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    requestLayout();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;很明显，在onChanged的末尾调用了requestLayout方法，而requestLayout方法是用来绘制界面的，定义在View中。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void requestLayout() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mMeasureCache != null) mMeasureCache.clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mAttachInfo != null &amp;amp;&amp;amp; mAttachInfo.mViewRequestingLayout == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Only trigger request-during-layout logic if this is the view requesting it,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // not the views in its parent hierarchy&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ViewRootImpl viewRoot = getViewRootImpl();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (viewRoot != null &amp;amp;&amp;amp; viewRoot.isInLayout()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (!viewRoot.requestLayoutDuringLayout(this)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                return;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAttachInfo.mViewRequestingLayout = this;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mPrivateFlags |= PFLAG_FORCE_LAYOUT;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mPrivateFlags |= PFLAG_INVALIDATED;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mParent != null &amp;amp;&amp;amp; !mParent.isLayoutRequested()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mParent.requestLayout();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mAttachInfo != null &amp;amp;&amp;amp; mAttachInfo.mViewRequestingLayout == this) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mAttachInfo.mViewRequestingLayout = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;根据上述解释，会发现notifyDataSetChanged会通知View刷新所有与其绑定的数据列表，而某些局部操作明显不需要全部刷新，全局刷新会造成极大的资源浪费。在这种情况下，就需要进行局部更新。&lt;/p&gt;
&lt;p&gt;局部更新的实现定义在是适配器（adapter）中，根据指定的index（即item在listview中的位置），实现指定条目内容的更新：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 局部刷新&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @param index item在listview中的位置 &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void updateItem(int index) &amp;#123;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (listView == null) &amp;#123;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return;  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 停止滑动时才更新界面&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 指定更新的位置在可见范围之内&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (index &amp;gt;= listView.getFirstVisiblePosition() &amp;amp;&amp;amp; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            index &amp;lt;= listView.getLastVisiblePosition()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            // 获取当前可以看到的item位置  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            int visiblePosition = listView.getFirstVisiblePosition();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            View view = listView.getChildAt(index - visiblePosition); &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            //在这里对view中的组件进行设置，数据可以通过getItem(index)获取//&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;（一个小问题：ListView的getCount()与getChildCount()有什么差别呢？）&lt;/p&gt;
&lt;p&gt;使用notifyDataSetChanged时，一个常见的问题就是调用了notifyDataSetChanged，但界面并没有刷新。很常见的原因是list的指向改变了，换句话说，list指向了与初始化时不同的堆地址。这种情况比较常见，给一个说明的链接：&lt;a href=&quot;http://www.tuicool.com/articles/aiiYzeR。&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.tuicool.com/articles/aiiYzeR。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一般的经验是在声明变量时对list进行初始化，当涉及数据改变时，通过add或者remove实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. listview的item内部组件的事件响应&lt;/strong&gt;&lt;br&gt;在具体的工程中，item组件的响应会根据对其使用的Activity（Fragment）的不同而变化，因此，不宜在其内部设定响应事件的具体实现。推荐在adapter中定义接口，将接口暴露给具体的Activity（Fragment），Activity（Fragment）根据具体的业务逻辑进行配置。以前面提到的论坛帖子为例，其包含以下操作：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 处理Item中控件的点击事件接口&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public interface ITopicItemOperation &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void topicItemEdit(BBSTopicItem item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void topicItemCollect(BBSTopicItem item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void topicItemReply(BBSTopicItem item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;然后， 在Activity（Fragment）中实现上述接口：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 编辑主题贴&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void topicItemEdit(BBSTopicItem item) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 业务逻辑&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 收藏主题贴&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void topicItemCollect(BBSTopicItem item) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 业务逻辑&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 回复主题帖&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void topicItemReply(BBSTopicItem item) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 业务逻辑&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;将该实现通过adapter的构造器进行传递，以响应点击事件为例：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 处理ListView中控件的点击事件&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private class TopicItemOnClickListener implements OnClickListener &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private BBSTopicItem item;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public TopicItemOnClickListener(BBSTopicItem item) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        this.item = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onClick(View v) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        switch (v.getId()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        case R.id.bbs_topic_edit:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            topicItemOperation.topicItemEdit(item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        case R.id.bbs_topic_collect:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            topicItemOperation.topicItemCollect(item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        case R.id.bbs_topic_reply:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            topicItemOperation.topicItemReply(item);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在点击按钮时，添加TopicItemOnClickListener对象，即可实现不同的Activity（Fragment）对该项功能的复用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.图文混合显示&lt;/strong&gt;&lt;br&gt;在涉及论坛帖子的时候，图文混合显示一种很常见的场景。Android中没有原生支持图文混合显示的控件，github上有一些自定义控件能实现这种需求，百度一下也能发现很多。但此类个性化的需求需要很据项目实际来灵活运用，这里描述一种通过正则来处理的方法。比如，服务端返回的帖子内容如下：&lt;/p&gt;
&lt;p&gt;“全新宝马7系上市了，是不是很有气势？ &lt;a href=&quot;http://img2.tuohuangzu.com/THZ/UserBlog/0/15/2015061810510550085.jpg&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://img2.tuohuangzu.com/THZ/UserBlog/0/15/2015061810510550085.jpg&lt;/a&gt; 不过相比于7系，我还是更喜欢3系的操控，转向非常精确，而且过弯姿势的建立也是非常恰到好处，过弯姿势建立的过早过晚都不好。过早会导致操控措手不及，无法感觉方向打多少，在匝道，有时打多了要在回，回多了又要打。过晚会导致路感缺失，侧倾明显，虚的慌 &lt;a href=&quot;http://res3.auto.ifeng.com/s/6606/0/3/13309355849880_3.jpghttp://img1.cheshi-img.com/product/1_1024/887/4b25a8df6e8ec.jpg&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://res3.auto.ifeng.com/s/6606/0/3/13309355849880_3.jpghttp://img1.cheshi-img.com/product/1_1024/887/4b25a8df6e8ec.jpg&lt;/a&gt; 明天天气不错，去自驾游如何？”&lt;/p&gt;
&lt;p&gt;帖子内容为纯文本格式，显示时需要从中提取出图片的链接。这时正则就派上用场了：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Pattern p = Pattern.compile(&amp;quot;http://[^\\u4e00-\\u9fa5]*?[.]jpg&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Matcher m = p.matcher(text);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int lastTextIndex = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;while (m.find()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 设置文本显示&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String textFrag = text.substring(lastTextIndex, m.start());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (!textFrag.isEmpty()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        layout.addView(getTextView(context, textFrag));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 更新最后文本下标&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    lastTextIndex = m.end();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 设置图片显示&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String imageUrl = m.group();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ImageView imageView = getImageView(context);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    setImageViewDisplay(imageView, imageUrl);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    layout.addView(imageView);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if (lastTextIndex &amp;lt; text.length()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String textFrag = text.substring(lastTextIndex, text.length());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    layout.addView(getTextView(context, textFrag));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;这段代码比较简单，只有一处需要说明。在上述帖子内容汇总，后面两张图片的链接是连续的，当正则表达式中包含能接受重复的限定符时，通常的行为是（在使整个表达式能得到匹配的前提下）匹配尽可能多的字符。以这个表达式为例：a.*b，它将会匹配最长的以a开始，以b结束的字符串。如果用它来搜索aabab的话，它会匹配整个字符串aabab。这被称为贪婪匹配。有时，我们更需要懒惰匹配，也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式，只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复，但是在能使整个匹配成功的前提下使用最少的重复。因此，在上述场景中，想要将连续的两个URL匹配成功，则需要进行懒惰匹配。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.ScrollView与ListView的冲突&lt;/strong&gt;&lt;br&gt;如果在ScrollView中嵌套了ListView（原则上应尽量避免这种情况），那么很不幸，可能会遇到以下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ListView只显示一行&lt;/li&gt;
&lt;li&gt;页面默认不从顶端开始显示&lt;/li&gt;
&lt;li&gt;ListView滑动事件无法监听&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这几个问题都是比较常见的问题了，这里不再赘述其原理，给出比较通用的解决方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;listview需要手动设置高度，这里给出一个链接：&lt;a href=&quot;http://www.cnblogs.com/zhwl/p/3333585.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://www.cnblogs.com/zhwl/p/3333585.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;listview需要设置listview.setFocusable(false);&lt;/li&gt;
&lt;li&gt;重载listview的onInterceptTouchEvent方法，在ACTION_DOWN时通过ScrollView的requestDisallowInterceptTouchEvent方法设置交出ontouch权限，ACTION_CANCEL时再恢复ontouch权限。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;再次强调，应尽量&lt;strong&gt;避免ScrollView中嵌套了ListView&lt;/strong&gt;。&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;ListView是Android开发中最常用的组件之一。本文将重点说明如何正确使用ListView，以及使用过程中可能遇到的问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ListView开发模块&lt;/li&gt;
&lt;li&gt;图片缓存&lt;/li&gt;
&lt;li&gt;可能遇到的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;hr
    
    </summary>
    
      <category term="Android开发笔记" scheme="http://yhthu.com/categories/Android%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="图片缓存" scheme="http://yhthu.com/tags/%E5%9B%BE%E7%89%87%E7%BC%93%E5%AD%98/"/>
    
      <category term="ListView" scheme="http://yhthu.com/tags/ListView/"/>
    
      <category term="notifyDataSetChanged" scheme="http://yhthu.com/tags/notifyDataSetChanged/"/>
    
  </entry>
  
  <entry>
    <title>Android开发笔记——视频录制播放常见问题</title>
    <link href="http://yhthu.com/2016/05/03/201605037/"/>
    <id>http://yhthu.com/2016/05/03/201605037/</id>
    <published>2016-05-03T10:00:44.773Z</published>
    <updated>2016-05-03T10:00:50.250Z</updated>
    
    <content type="html">&lt;p&gt;本文分享自己在视频录制播放过程中遇到的一些问题，主要包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;视频录制流程&lt;/li&gt;
&lt;li&gt;视频预览及SurfaceHolder&lt;/li&gt;
&lt;li&gt;视频清晰度及文件大小&lt;/li&gt;
&lt;li&gt;视频文件旋转&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、视频录制流程&quot;&gt;&lt;a href=&quot;#一、视频录制流程&quot; class=&quot;headerlink&quot; title=&quot;一、视频录制流程&quot;&gt;&lt;/a&gt;一、视频录制流程&lt;/h2&gt;&lt;p&gt;以微信为例，其录制触发为按下（住）录制按钮，结束录制的触发条件为松开录制按钮或录制时间结束，其流程大概可以用下图来描述。&lt;br&gt;&lt;img src=&quot;/img/20151230-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-1、开始录制&quot;&gt;&lt;a href=&quot;#1-1、开始录制&quot; class=&quot;headerlink&quot; title=&quot;1.1、开始录制&quot;&gt;&lt;/a&gt;1.1、开始录制&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;/img/20151230-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;初始化过程主要包括View，Data以及Listener三部分。在初始化View时，添加摄像头预览，添加倒计时文本组件，设置初始状态UI组件的可见；初始化Data时，从Intent中获取初始数据；初始化Listener时，分别对录制触发按钮，保存/取消视频录制按钮以及视频预览界面添加监听。&lt;br&gt;当系统初始化成功后，等待用户按下录制按钮，因此在录制按钮的监听中，需要完成以下功能：录制，计时，更新界面组件。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;if(isRecording) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.stop();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    releaseMediaRecorder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCamera.lock();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    isRecording = false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if(startRecordVideo()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    startTimeVideoRecord();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    isRecording = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;首先判断当前录制状态，如果正在录制，则先停止录制，释放MediaRecorder资源，锁定摄像头，置位录制状态；然后开始视频录制startRecordVideo，其boolean型返回值表征是否启动成功，启动成功后，开始视频录制计时，并且置位录制状态。startRecordVideo涉及MediaRecorder的配置，准备以及启动。&lt;br&gt;&lt;img src=&quot;/img/20151230-3.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;翻译成代码如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private boolean startRecordVideo() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    configureMediaRecorder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if(!prepareConfiguredMediaRecorder()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-2、结束录制&quot;&gt;&lt;a href=&quot;#1-2、结束录制&quot; class=&quot;headerlink&quot; title=&quot;1.2、结束录制&quot;&gt;&lt;/a&gt;1.2、结束录制&lt;/h3&gt;&lt;p&gt;根据上述流程图可知，结束录制的触发条件为松开录制按钮或计时时间到。在结束录制方法中，需要释放MediaRecorder，开始循环播放已录制视频，设置界面更新等。&lt;br&gt;&lt;img src=&quot;/img/20151230-4.png&quot; alt=&quot;&quot;&gt;&lt;br&gt; 翻译成代码如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private void stopRecordVideo() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    releaseMediaRecorder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 录制视频文件处理&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if(currentRecordProgress &amp;lt; MIN_RECORD_TIME) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Toast.makeText(VideoInputActivity.this, &amp;quot;录制时间太短&amp;quot;, Toast.LENGTH_SHORT).show();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        startVideoPlay();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        isPlaying = true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        setUiDisplayAfterVideoRecordFinish();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    currentRecordProgress = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    updateProgressBar(currentRecordProgress);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    releaseTimer();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 状态设置&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    isRecording = false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id=&quot;二、视频预览及SurfaceHolder&quot;&gt;&lt;a href=&quot;#二、视频预览及SurfaceHolder&quot; class=&quot;headerlink&quot; title=&quot;二、视频预览及SurfaceHolder&quot;&gt;&lt;/a&gt;二、视频预览及SurfaceHolder&lt;/h2&gt;&lt;p&gt;视频预览采用SurfaceView，相比于普通的View，SurfaceView在一个新起的单独线程中绘制画面，该实现的优点是更新画面不会阻塞UI主线程，缺点是会带来事件同步的问题。当然，这涉及到UI事件的传递以及线程同步，这里不做详细说明，有兴趣的可以参考链接：&lt;a href=&quot;http://wugengxin.cn/download/pdf/android/PRE_andevcon_mastering-the-android-touch-system.pdf。&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://wugengxin.cn/download/pdf/android/PRE_andevcon_mastering-the-android-touch-system.pdf。&lt;/a&gt;&lt;br&gt;在实现中，通过继承SurfaceView组件来实现自定义预览控件。首先，SurfaceView的getHolder()方法会返回SurfaceHolder，需要为SurfaceHolder添加SurfaceHolder.Callback回调；其次，重写surfaceCreated、surfaceChanged和surfaceDestroyed实现。&lt;/p&gt;
&lt;h3 id=&quot;2-1、构造器&quot;&gt;&lt;a href=&quot;#2-1、构造器&quot; class=&quot;headerlink&quot; title=&quot;2.1、构造器&quot;&gt;&lt;/a&gt;2.1、构造器&lt;/h3&gt;&lt;p&gt;构造器包含了初始化域以及添加上述回调的过程。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public CameraPreview(Context context, Camera camera) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super(context);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCamera = camera;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHolder = getHolder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHolder.addCallback(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;这里需要说明mSupportedPreviewSizes，由于摄像头支持的预览尺寸由Camera本身的参数决定，因此需要首先获取其所支持的预览尺寸。&lt;/p&gt;
&lt;h3 id=&quot;2-2、预览尺寸的设置&quot;&gt;&lt;a href=&quot;#2-2、预览尺寸的设置&quot; class=&quot;headerlink&quot; title=&quot;2.2、预览尺寸的设置&quot;&gt;&lt;/a&gt;2.2、预览尺寸的设置&lt;/h3&gt;&lt;p&gt;从Google官方的Camera示例程序中可以看出，选择预览尺寸的标准是（1）摄像头支持的预览尺寸的宽高比与SurfaceView的宽高比的绝对差值小于0.1；（2）在（1）获得的尺寸中，选取与SurfaceView的高的差值最小的。通过代码对这两个标准进行了实现，这里贴一下官方的代码：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public Camera.Size getOptimalPreviewSize(List&amp;lt;Camera.Size&amp;gt; sizes, int w, int h) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final double ASPECT_TOLERANCE = 0.1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    double targetRatio = (double) w / h;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (sizes == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Camera.Size optimalSize = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    double minDiff = Double.MAX_VALUE;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int targetHeight = h;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    for (Camera.Size size : sizes) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        double ratio = (double) size.width / size.height;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (Math.abs(ratio - targetRatio) &amp;gt; ASPECT_TOLERANCE)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            continue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (Math.abs(size.height - targetHeight) &amp;lt; minDiff) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            optimalSize = size;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            minDiff = Math.abs(size.height - targetHeight);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (optimalSize == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        minDiff = Double.MAX_VALUE;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        for (Camera.Size size : sizes) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (Math.abs(size.height - targetHeight) &amp;lt; minDiff) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                optimalSize = size;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                minDiff = Math.abs(size.height - targetHeight);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return optimalSize;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在加载预览画面时，需要考虑Camera支持的尺寸（getSupportedPreviewSizes）和加载预览画面的SurfaceView的尺寸（layout_width/layout_height），在预览阶段，两者之间的关系直接影响清晰度及图像拉伸。对于Camera的尺寸，由于设备的硬件差异，不同设备支持的尺寸存在差异，但在默认情况（orientation=landscape）下，其width&amp;gt;height。以HTC609d为例，Camera支持的分辨率为1280&lt;em&gt;720（16：9）……640&lt;/em&gt;480（4：3）……480&lt;em&gt;320（3：2）等十多种，而其屏幕的分辨率为960&lt;/em&gt;540（16：9）。因此，很容易得到以下结论：（1）当Camera预览尺寸小于SurfaceView尺寸较多时，预览画面就不清晰；（2）Camera预览尺寸宽高比与SurfaceView宽高比相差较大时，预览画面就会拉伸。&lt;br&gt;上述代码在手机设置为横屏时并没有问题，在设置为竖屏时，为获得最优的预览尺寸，需要在调用此方法前比较SurfaceView的宽高。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;if (mSupportedPreviewSizes != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        Math.max(width, height), Math.min(width, height));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;获得与当前SurfaceView匹配的预览尺寸后，即可通过Camera.Parameters进行设置。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Camera.Parameters mParams = mCamera.getParameters();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mCamera.setDisplayOrientation(90);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;List&amp;lt;String&amp;gt; focusModes = mParams.getSupportedFocusModes();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if(focusModes.contains(&amp;quot;continuous-video&amp;quot;))&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mCamera.setParameters(mParams);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id=&quot;三、视频清晰度及文件大小&quot;&gt;&lt;a href=&quot;#三、视频清晰度及文件大小&quot; class=&quot;headerlink&quot; title=&quot;三、视频清晰度及文件大小&quot;&gt;&lt;/a&gt;三、视频清晰度及文件大小&lt;/h2&gt;&lt;p&gt;在第一节中讲到startRecordVideo，包括配置MediaRecorder，准备MediaRecorder以及启动，其中配置MediaRecorder是视频录制的重点，需要了解每项配置参数的作用，根据业务场景灵活配置。这里参考Google官方的示例给出一个可行的配置方案，然后再对其进行解释。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private void configureMediaRecorder() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // BEGIN_INCLUDE (configure_media_recorder)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder = new MediaRecorder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Step 1: Unlock and set camera to MediaRecorder&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCamera.unlock();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setCamera(mCamera);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setOrientationHint(90);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Step 2: Set sources&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Step 3: Set a Camera Parameters&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /* Fixed video Size: 640 * 480*/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setVideoSize(640, 480);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /* Encoding bit rate: 1 * 1024 * 1024*/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Step 4: Set output file&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setMaxFileSize(maxFileSizeInBytes);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setOutputFile(videoFilePath);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // END_INCLUDE (configure_media_recorder)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Set MediaRecorder ErrorListener&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mMediaRecorder.setOnErrorListener(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1：&lt;/strong&gt;&lt;br&gt;setCamera参数能够使得在预览和录制中快速切换，避免Camera对象的重新加载。在某些Android手机自带的照相机程序中，切换预览与录制中的短暂卡顿，读者可自行体会。&lt;br&gt;mMediaRecorder.setOrientationHint(90)在录制方向为竖直（portrait）时使用，它能使视频文件的沿顺时针方向旋转90度，如果不设置此项，播放视频时，画面会发生90度的旋转。不过这里更重要的是，即使设置了此项，在某些播放器上，画面依然会有90度的旋转（比如将在手机上正常播放的视频导入到PC中进行播放，或者嵌入H5的video标签中），这可是为什么呢？注意setOrientationHint的说明：Note that some video players may choose to ignore the compostion matrix in a video during playback. 那么如何做到在所有播放器上都能以正常方向播放呢？稍等，后续专门对其进行说明。&lt;br&gt;&lt;strong&gt;Step 2：&lt;/strong&gt;&lt;br&gt;setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION)，VOICE_RECOGNITION相比于MIC会根据语音识别的需要做一些调谐，当然，这需要在系统支持的情况下。&lt;br&gt;setVideoSource自然是VideoSource.CAMERA，只是在此两项设置必须在设置编码器之前设置，这无需说明。&lt;br&gt;&lt;strong&gt;Step 3：&lt;/strong&gt;&lt;br&gt;setOutputFormat需要在Step 2之后，并且在prepare()之前。这里采用OutputFormat.MPEG_4格式。&lt;br&gt;setVideoSize需要权衡的因素较多，主要包括三方面：MediaRecorder支持的录制尺寸、视频文件的大小以及兼容不同Android机型。这里采用640 &lt;em&gt; 480（微信小视频的尺寸是320&lt;/em&gt;240），文件大小在500-1000kb之间，并且市面上99%以上机型支持此录制尺寸。&lt;br&gt;setVideoEncodingBitRate与视频的清晰度有关，设置此参数需要权衡清晰度与文件大小的关系。太高，文件大不易传输；太低，文件清晰度低，识别率低。需要根据实际业务场景灵活调整。&lt;br&gt;setVideoEncoder采用H264编码，MPEG4、H263、H264等不同编码的差别比较可参考&lt;a href=&quot;http://blog.csdn.net/wcl0715/article/details/676137，实际使用中，H264的压缩率较高，推荐使用。&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://blog.csdn.net/wcl0715/article/details/676137，实际使用中，H264的压缩率较高，推荐使用。&lt;/a&gt;&lt;br&gt;setAudioEncoder采用AudioEncoder.AAC，该设置主要是考虑其通用性、兼容性。&lt;br&gt;&lt;strong&gt;Step 4：&lt;/strong&gt;&lt;br&gt;setMaxFileSize指定录制文件的大小限制，当然还可以限制其最大录制时间。&lt;br&gt;setOutputFile指定输出视频的路径。&lt;br&gt;setOnErrorListener指定错误监听器。&lt;/p&gt;
&lt;p&gt;在完成上述配置之后，即可准备MediaRecorder，并在返回成功后开始视频录制。&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private boolean prepareConfiguredMediaRecorder() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Step 5: Prepare configured MediaRecorder&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mMediaRecorder.prepare();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        releaseMediaRecorder();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&quot;四、视频文件旋转&quot;&gt;&lt;a href=&quot;#四、视频文件旋转&quot; class=&quot;headerlink&quot; title=&quot;四、视频文件旋转&quot;&gt;&lt;/a&gt;四、视频文件旋转&lt;/h2&gt;&lt;p&gt;第三节中Step 1提到对视频文件的旋转，因为某些播放器会忽略录制视频时的配置参数，因此可尝试通过第三方库对视频文件进行旋转，例如：OpenCV，fastCV等，在Camera对象的Camera.PreviewCallback中截取每帧数据byte[] data，然后对其进行处理，然后输出。该方法需要考虑处理方法的高效性，在编程时一般采用NDK，在C++中完成关键的处理，这里贴出fastCV中该处理方法的逻辑。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void onPreviewFrame( byte[] data, Camera c ) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Increment FPS counter for camera.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    util.cameraFrameTick();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Perform processing on the camera preview data.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    update( data, mDesiredWidth, mDesiredHeight );&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Simple IIR filter on time.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mProcessTime = util.getFastCVProcessTime();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if( c != null ) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // with buffer requires addbuffer each callback frame.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        c.addCallbackBuffer( mPreviewBuffer );&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        c.setPreviewCallbackWithBuffer( this );&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Mark dirty for render.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    requestRender();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;其中，update为native方法，其实现由jni中对应的文件完成，其中调用了libfastcv.a中相应的API。这里涉及NDK编程的基本方法步骤：（1）开发环境；（2）编写Java代码、C/C++代码；（3）编译C/C++文件生成.so库；（4）重新编译工程，生成apk。由于本章不重点讲述NDK，这里不再展开。&lt;br&gt;除上述方法以外，笔者采用了另外一种思路进行了探索，上述方法处理的数据为每帧图像数据，可以理解为在线处理，而如果在录制完成之后再处理，可以理解为离线处理。这里采用了第三方库mp4parser，mp4parser是一款支持在Android中进行视频分割的库，这里通过其进行视频旋转。至于具体效果如何，读者有兴趣可自行尝试，这里留个悬念。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private boolean rotateVideoFileWithClockwiseDegree(String sourceFilePath, int degree) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if(!isFileAndDegreeValid(sourceFilePath, degree)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    rotateVideoFile(sourceFilePath, degree);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;对输入参数进行合法性检测之后，根据检测结果判断是否进行旋转。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private boolean isFileAndDegreeValid(String sourceFilePath, int degree) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if(sourceFilePath == null || (!sourceFilePath.endsWith(&amp;quot;.mp4&amp;quot;)) &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                              || (!new File(sourceFilePath).exists())) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (degree == 0 || (degree % 90 != 0)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return true;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private void rotateVideoFile(String sourceFilePath, int degree) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    List&amp;lt;TrackBox&amp;gt; trackBoxes = getTrackBoxesOfVideoFileByPath(sourceFilePath);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Movie rotatedMovie = getRotatedMovieOfTrackBox(trackBoxes);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    writeMovieToModifiedFile(rotatedMovie);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;通过mp4parser旋转视频主要分为三步：（1）获取视频文件对应的TrackBoxes；（2）根据TrackBoxes获取旋转后的Movie对象；（3）将Movie对象写入文件。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private List&amp;lt;TrackBox&amp;gt; getTrackBoxesOfVideoFileByPath(String sourceFilePath) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    IsoFile isoFile = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    List&amp;lt;TrackBox&amp;gt; trackBoxes = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        isoFile = new IsoFile(sourceFilePath);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        isoFile.close();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (IOException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return trackBoxes;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private Movie getRotatedMovieOfTrackBox(List&amp;lt;TrackBox&amp;gt; trackBoxes) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Movie rotatedMovie = new Movie();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 旋转&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    for (TrackBox trackBox : trackBoxes) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        trackBox.getTrackHeaderBox().setMatrix(Matrix.ROTATE_90);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        rotatedMovie.addTrack(new Mp4TrackImpl(trackBox));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return rotatedMovie;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private void writeMovieToModifiedFile(Movie movie) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Container container = new DefaultMp4Builder().build(movie);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    File modifiedVideoFile = new File(videoFilePath.replace(&amp;quot;.mp4&amp;quot;, &amp;quot;_MOD.mp4&amp;quot;));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    FileOutputStream fos;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        fos = new FileOutputStream(modifiedVideoFile);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        WritableByteChannel bb = Channels.newChannel(fos);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        container.writeContainer(bb);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 关闭文件流&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        fos.close();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;本文对Android视频录制中常见的问题进行了说明，转载请注明出处（虽然也没什么转载）。&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;本文分享自己在视频录制播放过程中遇到的一些问题，主要包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;视频录制流程&lt;/li&gt;
&lt;li&gt;视频预览及SurfaceHolder&lt;/li&gt;
&lt;li&gt;视频清晰度及文件大小&lt;/li&gt;
&lt;li&gt;视频文件旋转&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=
    
    </summary>
    
      <category term="Android开发笔记" scheme="http://yhthu.com/categories/Android%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="Android" scheme="http://yhthu.com/tags/Android/"/>
    
      <category term="视频录制播放" scheme="http://yhthu.com/tags/%E8%A7%86%E9%A2%91%E5%BD%95%E5%88%B6%E6%92%AD%E6%94%BE/"/>
    
  </entry>
  
  <entry>
    <title>Android开发笔记——以Volley图片加载、缓存、请求及展示为例理解Volley架构设计</title>
    <link href="http://yhthu.com/2016/05/03/201605035/"/>
    <id>http://yhthu.com/2016/05/03/201605035/</id>
    <published>2016-05-03T10:00:09.815Z</published>
    <updated>2016-05-03T10:00:17.292Z</updated>
    
    <content type="html">&lt;p&gt;Volley是由Google开源的、用于Android平台上的网络通信库。Volley通过优化Android的网络请求流程，形成了以&lt;strong&gt;Request-RequestQueue-Response&lt;/strong&gt;为主线的网络访问链，使得Android网络访问变得&lt;strong&gt;简单、高效、扩展性强&lt;/strong&gt;。（根据RTFSC原则，强烈建议Android的童鞋学习下Volley的架构设计）下面将以ImageLoader、ImageCache、ImageRequest及NetworkImageView为例，对此进行说明。&lt;/p&gt;
&lt;h2 id=&quot;一、ImageCache-ImageLoader-ImageListener&quot;&gt;&lt;a href=&quot;#一、ImageCache-ImageLoader-ImageListener&quot; class=&quot;headerlink&quot; title=&quot;一、ImageCache-ImageLoader-ImageListener&quot;&gt;&lt;/a&gt;一、ImageCache-ImageLoader-ImageListener&lt;/h2&gt;&lt;p&gt;对于图片的下载，这里采用自底向上的分析方法，即首先明确Volley加载图片是通过ImageLoader的get方法实现的，然后依次说明该方法需要的参数的构成。get方法有三种重载形式（早一些的版本没有第三种）：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public ImageContainer get(String requestUrl, final ImageListener listener);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight, ScaleType scaleType);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt; 不过前两种都是通过三种方法实现的，这里以第三种方法为例进行说明：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public ImageContainer get(String requestUrl, ImageListener imageListener,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        int maxWidth, int maxHeight, ScaleType scaleType) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // only fulfill requests that were initiated from the main thread.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    throwIfNotOnMainThread();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Try to look up the request in the cache of remote images.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Bitmap cachedBitmap = mCache.getBitmap(cacheKey);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (cachedBitmap != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Return the cached bitmap.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        imageListener.onResponse(container, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return container;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // The bitmap did not exist in the cache, fetch it!&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ImageContainer imageContainer =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            new ImageContainer(null, requestUrl, cacheKey, imageListener);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Update the caller to let them know that they should use the default bitmap.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    imageListener.onResponse(imageContainer, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // Check to see if a request is already in-flight.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    BatchedImageRequest request = mInFlightRequests.get(cacheKey);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (request != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // If it is, add this request to the list of listeners.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        request.addContainer(imageContainer);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return imageContainer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // The request is not already in flight. Send the new request to the network and&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // track it.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Request&amp;lt;Bitmap&amp;gt; newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            cacheKey);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mRequestQueue.add(newRequest);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mInFlightRequests.put(cacheKey,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            new BatchedImageRequest(newRequest, imageContainer));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return imageContainer;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;上述代码的逻辑非常清晰，可以下面的流程图来表示，不再赘述：&lt;br&gt;&lt;img src=&quot;/img/20151212-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于StringRequest、JsonRequest，Volley也采用了同样的处理流程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从get方法的形参入手，这里着重说明ImageListener（其它的形参见名知义）。ImageListener是ImageLoader的内部接口，继承于ErrorListener，需要实现的方法为onResponse：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public interface ImageListener extends ErrorListener &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * Listens for non-error changes to the loading of the image request.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @param response Holds all information pertaining to the request, as well&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * as the bitmap (if it is loaded).&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @param isImmediate True if this was called during ImageLoader.get() variants.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * This can be used to differentiate between a cached image loading and a network&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * image loading in order to, for example, run an animation to fade in network loaded&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * images.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onResponse(ImageContainer response, boolean isImmediate);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;ImageLoader还提供了静态方法getImageListener来获取ImageListener实例：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static ImageListener getImageListener(final ImageView view,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        final int defaultImageResId, final int errorImageResId) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return new ImageListener() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        public void onErrorResponse(VolleyError error) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (errorImageResId != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                view.setImageResource(errorImageResId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        public void onResponse(ImageContainer response, boolean isImmediate) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (response.getBitmap() != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                view.setImageBitmap(response.getBitmap());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125; else if (defaultImageResId != 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                view.setImageResource(defaultImageResId);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;从代码很容易看出，ImageListener就是Image请求返回时的回调接口，onErrorResponse和onResponse分别实现了请求失败和成功时加载对应的图片。&lt;br&gt;分析完get方法执行的流程及形参之后，我们回到ImageLoader本身。ImageLoader的构造函数如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Constructs a new ImageLoader.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @param queue The RequestQueue to use for making image requests.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @param imageCache The cache to use as an L1 cache.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public ImageLoader(RequestQueue queue, ImageCache imageCache) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mRequestQueue = queue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCache = imageCache;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;同样，从ImageLoader的形参入手，这里需要传入RequestQueue和ImageCache的实例对象。RequestQueue即整个Volley的核心请求队列，在使用Volley时第一个初始化的对象。其构造方法在Volley源码toolbox文件夹下的Volley工具类中：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class Volley &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /** Default on-disk cache directory. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private static final String DEFAULT_CACHE_DIR = &amp;quot;volley&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * Creates a default instance of the worker pool and calls &amp;#123;@link RequestQueue#start()&amp;#125; on it.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @param context A &amp;#123;@link Context&amp;#125; to use for creating the cache dir.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @param stack An &amp;#123;@link HttpStack&amp;#125; to use for the network, or null for default.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @return A started &amp;#123;@link RequestQueue&amp;#125; instance.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public static RequestQueue newRequestQueue(Context context, HttpStack stack) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        String userAgent = &amp;quot;volley/0&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            String packageName = context.getPackageName();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            userAgent = packageName + &amp;quot;/&amp;quot; + info.versionCode;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; catch (NameNotFoundException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (stack == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (Build.VERSION.SDK_INT &amp;gt;= 9) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                stack = new HurlStack();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                // Prior to Gingerbread, HttpUrlConnection was unreliable.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Network network = new BasicNetwork(stack);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        queue.start();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return queue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * Creates a default instance of the worker pool and calls &amp;#123;@link RequestQueue#start()&amp;#125; on it.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @param context A &amp;#123;@link Context&amp;#125; to use for creating the cache dir.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     * @return A started &amp;#123;@link RequestQueue&amp;#125; instance.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public static RequestQueue newRequestQueue(Context context) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return newRequestQueue(context, null);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;这里重点说明ImageCache，即图片的缓存。ImageCache同样是定义在ImageLoader中的接口（从这里也可以看出volley的高可扩展性）：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Simple cache adapter interface. If provided to the ImageLoader, it&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * will be used as an L1 cache before dispatch to Volley. Implementations&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * must not block. Implementation with an LruCache is recommended.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public interface ImageCache &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public Bitmap getBitmap(String url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void putBitmap(String url, Bitmap bitmap);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;根据注释可以看出，推荐使用LruCache。对于LruCache，前面的博文《Android开发笔记——ListView模块、缓存及性能》已做过详细介绍，其通过维护一个强引用来限制内容数量，每当Item被访问的时候，此Item就会移动到队列的头部。当cache已满时加入新的item，在队列尾部的item会被回收。&lt;br&gt;不过，在某些应用场景下，只使用LruCache还不够。当应用退出后，LruCache清空，重新加载时，缓存的图片依然需要重新加载。这里需要使用DiskLruCache，即磁盘缓存，原理与《Android开发笔记——ListView模块、缓存及性能》中SD卡存储配合LruCache相同，但DiskLruCache实现更为合理，获得Google官方认证。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于DiskLruCache的源码解析，推荐Android DiskLruCache完全解析，硬盘缓存的最佳方案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此，建议配合LruCache和DiskLruCache，以及Volley的请求缓存，形成图片三级缓存。LruCache和DiskLruCache的初始化方法分别如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 实例化LruCaceh对象&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mLruCache = new LruCache&amp;lt;String, Bitmap&amp;gt;(maxSize) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     protected int sizeOf(String key, Bitmap bitmap) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;          return bitmap.getRowBytes() * bitmap.getHeight();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// 实例化DiskLruCache对象&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;      // 获取DiskLruCahce对象&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;      mDiskLruCache = DiskLruCache.open(getDiskCacheDir(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    context.getApplicationContext(), &amp;quot;younghao&amp;quot;), getAppVersion(context), 1, DISKMAXSIZE);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     &amp;#125; catch (IOException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;ImageCaches实现getBitmap和putBitmap方法时，可以使用LruCache和DiskLruCache实例高效处理图片。存入缓存时，先存入到LruCache中，然后判断是否存在DiskLruCache缓存，若没有存入；读取图片时，先从LruCache中取，没有时再从DiskLruCache中取（取出之后，顺便存入LruCache中，供下次访问时使用，不再访问DiskLruCache）。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 存入缓存（内存缓存，磁盘缓存）&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void putBitmap(String url, Bitmap bitmap) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 存入LruCache缓存&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mLruCache.put(url, bitmap);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 判断是否存在DiskLruCache缓存，若没有存入&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String key = MD5Utils.md5(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (mDiskLruCache.get(key) == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            DiskLruCache.Editor editor = mDiskLruCache.edit(key);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (editor != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                OutputStream outputStream = editor.newOutputStream(0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                if (bitmap.compress(CompressFormat.JPEG, 100, outputStream)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    editor.commit();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    editor.abort();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            mDiskLruCache.flush();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; catch (IOException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * 从缓存（内存缓存，磁盘缓存）中获取Bitmap&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public Bitmap getBitmap(String url) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mLruCache.get(url) != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 从LruCache缓存中取&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Log.i(TAG, &amp;quot;从LruCahce获取&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return mLruCache.get(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        String key = MD5Utils.md5(url);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (mDiskLruCache.get(key) != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                // 从DiskLruCahce取&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                Bitmap bitmap = null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                if (snapshot != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    // 存入LruCache缓存&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    mLruCache.put(url, bitmap);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    Log.i(TAG, &amp;quot;从DiskLruCahce获取&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                return bitmap;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125; catch (IOException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            e.printStackTrace();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return null;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;至此，通过volley加载图片的方法已完成。调用方法如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// 获取ImageCache实例&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;ImageCacheUtil imageCacheUtil = ImageCacheUtil.instance(context);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 初始化ImageLoader实例&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;ImageLoader imageLoader = new ImageLoader(requestQueue, imageCacheUtil);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 获取ImageListener实例&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;ImageListener listener = ImageLoader.getImageListener(imageRequestBean.getImageView(),&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        imageRequestBean.getDefaultImageID(), imageRequestBean.getErrorImageID());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 发送请求图片&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;imageLoader.get(imageRequestBean.getUrl(),                 listener,imageRequestBean.getMaxWidth(), imageRequestBean.getMaxHeight());&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h2 id=&quot;二、对Volley的架构的理解&quot;&gt;&lt;a href=&quot;#二、对Volley的架构的理解&quot; class=&quot;headerlink&quot; title=&quot;二、对Volley的架构的理解&quot;&gt;&lt;/a&gt;二、对Volley的架构的理解&lt;/h2&gt;&lt;p&gt;如果有童鞋能读到这里，那么对Volley的网络请求处理逻辑应该已经有了一定的认识。下面将通过Volley的官方文档对Volley的架构作进一步的说明。Volley提供了对于网络请求的自动调度，能够处理高并发网络链接，拥有透明的磁盘及内存缓存，支持请求优先级、取消请求、异步网络请求等。Volley的源码地址为：&lt;a href=&quot;https://android.googlesource.com/platform/frameworks/volley（git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://android.googlesource.com/platform/frameworks/volley（git&lt;/a&gt; clone），不过考虑到网络问题，也可到github上下载：&lt;a href=&quot;https://github.com/mcxiaoke/android-volley.git（git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/mcxiaoke/android-volley.git（git&lt;/a&gt; clone）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2013年Volley发布会视频：Google I/O 2013 - Volley: Easy, Fast Networking for Android（YouTube，你懂）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2-1-发送一个简单的请求&quot;&gt;&lt;a href=&quot;#2-1-发送一个简单的请求&quot; class=&quot;headerlink&quot; title=&quot;2.1 发送一个简单的请求&quot;&gt;&lt;/a&gt;2.1 发送一个简单的请求&lt;/h3&gt;&lt;p&gt;先贴一张官网的图，后面做解释。&lt;br&gt;&lt;img src=&quot;/img/20151212-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;这张图展示了Volley的核心架构，主要包含了以下类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Volley。前面已经提到，位于toolbox文件中，是创建请求队列的工具类；&lt;/li&gt;
&lt;li&gt;Request。实现了Comparable&lt;request&lt;t&gt;&amp;gt;接口的抽象类，Volley中的请求都是继承于该类实现的，Request支持八种请求方法。&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public abstract class Request&amp;lt;T&amp;gt; implements Comparable&amp;lt;Request&amp;lt;T&amp;gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/request&lt;t&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Supported request methods.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public interface Method &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int DEPRECATED_GET_OR_POST = -1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int GET = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int POST = 1;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int PUT = 2;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int DELETE = 3;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int HEAD = 4;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int OPTIONS = 5;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int TRACE = 6;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    int PATCH = 7;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;RequestQueue。Volley的核心，代表整个请求队列。需要重点说明的是其成员变量，包含了&lt;ul&gt;
&lt;li&gt;CacheDispatcher（处理缓存请求的调度线程）&lt;/li&gt;
&lt;li&gt;NetworkDispatcher[]（处理网络请求的调用线程组）&lt;/li&gt;
&lt;li&gt;ResponseDelivery（网络请求返回接口分发）&lt;/li&gt;
&lt;li&gt;Network （执行网络请求的网络接口）&lt;/li&gt;
&lt;li&gt;Cache（缓存请求的接口，PS：上一节说的是缓存图片）&lt;/li&gt;
&lt;li&gt;DEFAULT_NETWORK_THREAD_POOL_SIZE（默认线程池数目，至于为什么是4？这是一个经验值，在自己实际应用中，可根据任务、网络状况以及设备等灵活设置）&lt;/li&gt;
&lt;li&gt;PriorityBlockingQueue&lt;request&lt;?&gt;&amp;gt; （基于优先级阻塞的请求队列，包含等待的和正在执行的）、&lt;/request&lt;?&gt;&lt;/li&gt;
&lt;li&gt;Set&lt;request&lt;?&gt;&amp;gt; mCurrentRequests （正在处理的请求）&lt;/request&lt;?&gt;&lt;/li&gt;
&lt;li&gt;Map&lt;string, queue&lt;request&lt;?=&quot;&quot;&gt;&amp;gt;&amp;gt; mWaitingRequests（正在等待的请求）&lt;/string,&gt;&lt;/li&gt;
&lt;li&gt;AtomicInteger mSequenceGenerator（原子的，避免并发访问）&lt;/li&gt;
&lt;li&gt;RequestFinishedListener&lt;t&gt;接口（请求完成的回调）&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;57&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/** Callback interface for completed requests. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public static interface RequestFinishedListener&amp;lt;T&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    /** Called when a request has finished processing. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onRequestFinished(Request&amp;lt;T&amp;gt; request);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** Used for generating monotonically-increasing sequence numbers for requests. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private AtomicInteger mSequenceGenerator = new AtomicInteger();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Staging area for requests that already have a duplicate request in flight.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;lt;ul&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *     &amp;lt;li&amp;gt;containsKey(cacheKey) indicates that there is a request in flight for the given cache&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *          key.&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *     &amp;lt;li&amp;gt;get(cacheKey) returns waiting requests for the given cache key. The in flight request&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *          is &amp;lt;em&amp;gt;not&amp;lt;/em&amp;gt; contained in that list. Is null if no requests are staged.&amp;lt;/li&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final Map&amp;lt;String, Queue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt;&amp;gt; mWaitingRequests =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        new HashMap&amp;lt;String, Queue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * The set of all requests currently being processed by this RequestQueue. A Request&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * will be in this set if it is waiting in any queue or currently being processed by&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * any dispatcher.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final Set&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt; mCurrentRequests = new HashSet&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** The cache triage queue. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final PriorityBlockingQueue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt; mCacheQueue =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    new PriorityBlockingQueue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** The queue of requests that are actually going out to the network. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final PriorityBlockingQueue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt; mNetworkQueue =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    new PriorityBlockingQueue&amp;lt;Request&amp;lt;?&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** Number of network request dispatcher threads to start. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** Cache interface for retrieving and storing responses. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final Cache mCache;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** Network interface for performing requests. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final Network mNetwork;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** Response delivery mechanism. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private final ResponseDelivery mDelivery;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** The network dispatchers. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private NetworkDispatcher[] mDispatchers;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/** The cache dispatcher. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private CacheDispatcher mCacheDispatcher;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;private List&amp;lt;RequestFinishedListener&amp;gt; mFinishedListeners =&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        new ArrayList&amp;lt;RequestFinishedListener&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/t&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;发送一个请求，只需将请求添加到请求队列即可。官网的一段示例代码如下：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;final TextView mTextView = (TextView) findViewById(R.id.text);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Instantiate the RequestQueue.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;RequestQueue queue = Volley.newRequestQueue(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;String url =&amp;quot;http://www.google.com&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Request a string response from the provided URL.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;StringRequest stringRequest = new StringRequest(Request.Method.GET, url,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            new Response.Listener&amp;lt;String&amp;gt;() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onResponse(String response) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // Display the first 500 characters of the response string.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mTextView.setText(&amp;quot;Response is: &amp;quot;+ response.substring(0,500));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;, new Response.ErrorListener() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onErrorResponse(VolleyError error) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mTextView.setText(&amp;quot;That didn&amp;apos;t work!&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Add the request to the RequestQueue.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;queue.add(stringRequest);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;取消请求同样简单，通过TAG来找到特定的request，然后取消。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static final String TAG = &amp;quot;MyTag&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;StringRequest stringRequest; // Assume this exists.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;RequestQueue mRequestQueue;  // Assume this exists.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Set the tag on the request.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;stringRequest.setTag(TAG);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Add the request to the RequestQueue.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mRequestQueue.add(stringRequest);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;protected void onStop () &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super.onStop();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (mRequestQueue != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mRequestQueue.cancelAll(TAG);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&quot;2-2-创建一个RequestQueue&quot;&gt;&lt;a href=&quot;#2-2-创建一个RequestQueue&quot; class=&quot;headerlink&quot; title=&quot;2.2 创建一个RequestQueue&quot;&gt;&lt;/a&gt;2.2 创建一个RequestQueue&lt;/h3&gt;&lt;p&gt; 在2.1节中，使用了默认的RequestQueue构造器（即通过Volley工具类），但Volley支持自定义NetWork和Cache，实现更加个性化的网络和缓存。另外一个需要注意的问题就是单例模式，为了高效的使用RequestQueue，官方建议在整个应用的生命周期内只使用一个RequestQueue实例。&lt;strong&gt;不过注意到很多中文的帖子使用继承Application，在Application的onCreate()方法中创建RequestQueue，官方并不鼓励这种做法，使用静态的的单例能够以更加模块化的方式实现同样的功能。&lt;/strong&gt;核心的思想是RequestQueue应该被Application的context实例，而不是某个Activity的context。（getApplicationContext()与getContext()方法的区别）&lt;/p&gt;
&lt;h3 id=&quot;2-3-创建一个标准的请求&quot;&gt;&lt;a href=&quot;#2-3-创建一个标准的请求&quot; class=&quot;headerlink&quot; title=&quot;2.3 创建一个标准的请求&quot;&gt;&lt;/a&gt;2.3 创建一个标准的请求&lt;/h3&gt;&lt;p&gt;继承Request的请求类型主要有：StringRequest、ImageRequest、JsonObjectRequest和JsonArrayRequest（JsonRequest的子类），第一节对ImageRequest进行了详细的说明，这里主要说明JsonObjectRequest和JsonArrayRequest，因为在实际项目中，通Json传递数据可能是目前最常见的方式。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;TextView mTxtDisplay;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;ImageView mImageView;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;String url = &amp;quot;http://my-json-feed&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;JsonObjectRequest jsObjRequest = new JsonObjectRequest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        (Request.Method.GET, url, null, new Response.Listener&amp;lt;JSONObject&amp;gt;() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onResponse(JSONObject response) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mTxtDisplay.setText(&amp;quot;Response: &amp;quot; + response.toString());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;, new Response.ErrorListener() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onErrorResponse(VolleyError error) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // TODO Auto-generated method stub&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// Access the RequestQueue through your singleton class.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt; 官方的示例代码比较简单。不过可以关注下Request的方法，它抽象了StringRequest、ImageRequest、JsonRequest的公共特征，这种结构设计的思路值得学习。&lt;/p&gt;
&lt;p&gt;最后推荐几个博客作为参考：&lt;br&gt;&lt;a href=&quot;http://p.codekk.com/blogs/detail/54cfab086c4761e5001b2542&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Volley 源码解析&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://bxbxbai.github.io/2014/09/14/android-working-with-volley/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Android库Volley的使用介绍&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;http://blog.csdn.net/guolin_blog/article/details/17656437&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Android Volley完全解析(四)，带你从源码的角度理解Volley（Volley系列文章）&lt;/a&gt;&lt;/p&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;Volley是由Google开源的、用于Android平台上的网络通信库。Volley通过优化Android的网络请求流程，形成了以&lt;strong&gt;Request-RequestQueue-Response&lt;/strong&gt;为主线的网络访问链，使得Android网络访问变得
    
    </summary>
    
      <category term="Android开发笔记" scheme="http://yhthu.com/categories/Android%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="Volley架构" scheme="http://yhthu.com/tags/Volley%E6%9E%B6%E6%9E%84/"/>
    
      <category term="图片缓存" scheme="http://yhthu.com/tags/%E5%9B%BE%E7%89%87%E7%BC%93%E5%AD%98/"/>
    
  </entry>
  
  <entry>
    <title>Android线程管理（一）——线程通信</title>
    <link href="http://yhthu.com/2016/05/03/201605032/"/>
    <id>http://yhthu.com/2016/05/03/201605032/</id>
    <published>2016-05-03T09:57:54.166Z</published>
    <updated>2016-05-03T09:57:59.065Z</updated>
    
    <content type="html">&lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;p&gt;《Android线程管理（一）——线程通信》&lt;br&gt;《Android线程管理（二）——ActivityThread》&lt;br&gt;《Android线程管理（三）——Thread》&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、Handler、MessageQueue、Message及Looper四者的关系&quot;&gt;&lt;a href=&quot;#一、Handler、MessageQueue、Message及Looper四者的关系&quot; class=&quot;headerlink&quot; title=&quot;一、Handler、MessageQueue、Message及Looper四者的关系&quot;&gt;&lt;/a&gt;一、Handler、MessageQueue、Message及Looper四者的关系&lt;/h2&gt;&lt;p&gt;在开发Android多线程应用时，Handler、MessageQueue、Message及Looper是老生常谈的话题。但想彻底理清它们之间的关系，却需要深入的研究下它们各自的实现才行。首先，给出一张它们之间的关系图：&lt;br&gt;&lt;img src=&quot;/img/20160113-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Looper依赖于MessageQueue和Thread，因为每个Thread只对应一个Looper，每个Looper只对应一个MessageQueue。&lt;/li&gt;
&lt;li&gt;MessageQueue依赖于Message，每个MessageQueue对应多个Message。即Message被压入MessageQueue中，形成一个Message集合。&lt;/li&gt;
&lt;li&gt;Message依赖于Handler进行处理，且每个Message最多指定一个Handler来处理。Handler依赖于MessageQueue、Looper及Callback。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从运行机制来看，Handler将Message压入MessageQueue，Looper不断从MessageQueue中取出Message（当MessageQueue为空时，进入休眠状态），其target handler则进行消息处理。因此，要彻底弄清Android的线程通信机制，需要了解以下三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handler的消息分发、处理流程&lt;/li&gt;
&lt;li&gt;MessageQueue的属性及操作&lt;/li&gt;
&lt;li&gt;Looper的工作原理&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;1-1-Handler的消息分发、处理流程&quot;&gt;&lt;a href=&quot;#1-1-Handler的消息分发、处理流程&quot; class=&quot;headerlink&quot; title=&quot;1.1 Handler的消息分发、处理流程&quot;&gt;&lt;/a&gt;1.1 Handler的消息分发、处理流程&lt;/h3&gt;&lt;p&gt;Handler主要完成Message的入队（MessageQueue）和处理，下面将通过Handler的源码分析其消息分发、处理流程。首先，来看下Handler类的方法列表：&lt;br&gt;&lt;img src=&quot;/img/20160113-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;从上图中可以看出，Handler类核心的方法包括：1）构造器；2）分发消息；3）处理消息；4）post发送消息；5）send发送消息；6）remove消息和回调。&lt;/p&gt;
&lt;p&gt;首先，从构造方法来看，构造器的多态最终通过调用如下方法实现，即将实参赋值给Handler类的内部域。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;final MessageQueue mQueue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;final Looper mLooper;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;final Callback mCallback;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;final boolean mAsynchronous;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public Handler(Looper looper, Callback callback, boolean async) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mLooper = looper;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mQueue = looper.mQueue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mCallback = callback;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mAsynchronous = async;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;其次，消息的入队是通过post方法和send方法来实现的。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public final boolean postAtTime(Runnable r, long uptimeMillis) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return sendMessageAtTime(getPostMessage(r), uptimeMillis);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Message msg = Message.obtain();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    msg.what = what;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return sendMessageAtTime(msg, uptimeMillis);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public boolean sendMessageAtTime(Message msg, long uptimeMillis) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    MessageQueue queue = mQueue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (queue == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        RuntimeException e = new RuntimeException(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                this + &amp;quot; sendMessageAtTime() called with no mQueue&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Log.w(&amp;quot;Looper&amp;quot;, e.getMessage(), e);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        return false;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return enqueueMessage(queue, msg, uptimeMillis);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;两者的区别在于参数类型不同，post方法传入的实例对象实现了Runnable接口，然后在内部通过getPostMessage方法将其转换为Message，最终通过send方法发出；send方法传入的实例对象为Message类型，在实现中，将Message压入MessageQueue。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private static Message getPostMessage(Runnable r) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    Message m = Message.obtain();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    m.callback = r;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return m;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;通过Handler将Message压入MessageQueue之后，Looper将其轮询后交由Message的target handler处理。Handler首先会对消息进行分发。首先判断Message的回调处理接口Callback是否为null，不为null则调用该Callback进行处理；否判断Handler的回调接口mCallback是否为null，不为null则调用该Callback进行处理；如果上述Callback均为null，则调用handleMessage方法处理。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public void dispatchMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (msg.callback != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        handleCallback(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125; else &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        if (mCallback != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            if (mCallback.handleMessage(msg)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                return;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;handleMessage方法在Handler的子类中必须实现。即消息具体的处理交由应用软件实现。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Subclasses must implement this to receive messages.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;回到Activity（Fragment），在Handler的子类中实现handleMessage方法。这里需要注意一个内存泄露的问题，比较下述两种实现方式，第一种直接定义Handler的实现，第二种通过静态内部类继承Handler，定义继承类的实例。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Handler mHandler = new Handler() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        super.handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 根据msg调用Activity的方法&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;static class MyHandler extends Handler &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    WeakReference&amp;lt;DemoActivity&amp;gt; mActivity;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public MyHandler(DemoActivity demoActivity) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mActivity = new WeakReference&amp;lt;DemoActivity&amp;gt;(demoActivity);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        super.handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        DemoActivity theActivity = mActivity.get();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        // 根据msg调用theActivity的方法&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;不绕弯子，直接说明为什么第一种方式会引起内存泄露，而第二种不会。&lt;/p&gt;
&lt;p&gt;在第一种方式中，mHandler通过匿名内部类方式实例化，在Java中，内部类会强持有外部类的引用（handleMessage方法中可以直接调用Activity的方法），在外部Activity调用onDestroy()方法之后，如果Handler的MessageQueue依然有未处理的消息，那么由于Handler持有Activity的引用导致Activity无法被系统GC回收，从而引起内存泄露。&lt;/p&gt;
&lt;p&gt;在第二种方式中，首先继承Handler定义静态内部类，由于MyHandler为静态类，即使定义在Activity的内部，也与Activity没有逻辑上的联系，即不会持有外部Activity的引用；其次，在静态类内部，定义外部Activity的弱引用，弱引用在系统资源紧张时会被系统优先回收。最后，在handleMessage()方法中，通过WeakReference的get方法获取外部Activity的引用，如果该弱引用已被回收，则get方法返回null。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;struct GcSpec &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  /* If true, only the application heap is threatened. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  bool isPartial;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  /* If true, the trace is run concurrently with the mutator. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  bool isConcurrent;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  /* Toggles for the soft reference clearing policy. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  bool doPreserve;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  /* A name for this garbage collection mode. */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  const char *reason;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;这段代码定义在dalvik/vm/alloc/Heap.h中，其中doPreserve为true时，表示在执行GC的过程中，不回收软引用引用的对象；为false时，表示在执行GC的过程中，回收软引用引用的对象。&lt;/p&gt;
&lt;p&gt;最后，使用Handler的过程中，还需要注意一点，在前面的方法列表图中已经提到。为避免Activity调用onDestroy后，Handler的MessageQueue中仍存在Message，一般会在onDestroy中调用removeCallbacksAndMessages()方法。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;protected void onDestroy() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super.onDestroy();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    // 清空Message队列&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    myHandler.removeCallbacksAndMessages(null);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public final void removeCallbacksAndMessages(Object token) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mQueue.removeCallbacksAndMessages(this, token);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;removeCallbacksAndMessages()方法会移除obj为token的由post发送的callback和send发送的message，当token为null时，会移除所有callback和message。&lt;/p&gt;
&lt;h3 id=&quot;1-2-MessageQueue的属性及操作&quot;&gt;&lt;a href=&quot;#1-2-MessageQueue的属性及操作&quot; class=&quot;headerlink&quot; title=&quot;1.2 MessageQueue的属性及操作&quot;&gt;&lt;/a&gt;1.2 MessageQueue的属性及操作&lt;/h3&gt;&lt;p&gt;MessageQueue，消息队列，其属性与常规队列相似，包括入队、出队等，这里简要介绍一下MessageQueue的实现。&lt;/p&gt;
&lt;p&gt;首先，MessageQueue新建队列的工作是通过在其构造器中调用本地方法nativeInit实现的。nativeInit会创建NativeMessageQueue对象，然后赋值给MessageQueue成员变量mPtr。mPtr是int类型数据，代表NativeMessageQueue的内存指针。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;MessageQueue(boolean quitAllowed) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mQuitAllowed = quitAllowed;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    mPtr = nativeInit();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;其次，Message入队的通过enqueueMessage方法实现。首先检查message是否符合入队要求（是否正在使用，target handler是否为null），符合要求后通过设置prev.next = msg队列的指针完成入队操作。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;boolean enqueueMessage(Message msg, long when);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;再次，出队是通过next()方法完成的。涉及到同步、锁等问题，这里不详细展开了。&lt;/p&gt;
&lt;p&gt;再次，删除元素有两个实现。即分别通过p.callback == r和p.what == what来进行消息识别。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;void removeMessages(Handler h, int what, Object object);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;void removeMessages(Handler h, Runnable r, Object object);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;最后，销毁队列和创建队列一样，是通过本地函数完成的。传入的参数为MessageQueue的内存指针。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private native static void nativeDestroy(int ptr);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-3-Looper的工作原理&quot;&gt;&lt;a href=&quot;#1-3-Looper的工作原理&quot; class=&quot;headerlink&quot; title=&quot;1.3 Looper的工作原理&quot;&gt;&lt;/a&gt;1.3 Looper的工作原理&lt;/h3&gt;&lt;p&gt;Looper是线程通信的关键，正是因为Looper，整个线程通信机制才真正实现“通”。&lt;/p&gt;
&lt;p&gt;在应用开发过程中，一般当主线程需要传递消息给用户自定义线程时，会在自定义线程中定义Handler进行消息处理，并在Handler实现的前后分别调用Looper的prepare()方法和loop()方法。大致实现如下：&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;new Thread(new Runnable() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Handler mHandler;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Looper.prepare();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        mHandler = new Handler() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            public void handleMessage(Message msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                super.handleMessage(msg);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        Looper.loop();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;这里重点说明prepare()方法和loop()方法，实际项目中不建议定义匿名线程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;private static void prepare(boolean quitAllowed) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    if (sThreadLocal.get() != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        throw new RuntimeException(&amp;quot;Only one Looper may be created per thread&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    sThreadLocal.set(new Looper(quitAllowed));&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;可以看出，prepare方法的重点是sThreadLocal变量，sThreadLocal变量是什么呢？&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;// sThreadLocal.get() will return null unless you&amp;apos;ve called prepare().&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;static final ThreadLocal&amp;lt;Looper&amp;gt; sThreadLocal = new ThreadLocal&amp;lt;Looper&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;ThreadLocal实现了线程本地存储。简单看一下它的类注解文档，ThreadLocal是一种特殊的全局变量，全局性在于它存储于自己所在线程相关的数据，而其他线程无法访问。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * Implements a thread-local storage, that is, a variable for which each thread&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * has its own value. All threads share the same &amp;#123;@code ThreadLocal&amp;#125; object,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * but each sees a different value when accessing it, and changes made by one&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * thread do not affect the other threads. The implementation supports&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * &amp;#123;@code null&amp;#125; values.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @see java.lang.Thread&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; * @author Bob Lee&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;public class ThreadLocal&amp;lt;T&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;回到prepare方法中，sThreadLocal添加了一个针对当前线程的Looper对象。并且prepare方法只能调用一次，否则会抛出运行时异常。&lt;/p&gt;
&lt;p&gt;初始化完毕之后，Handler通过post和send方法如何保证消息投递到Looper所持有的MessageQueue中呢？其实，MessageQueue是Handler和Looper的桥梁。在前面Handler章节中提到Handler的初始化方法，Handler的mLooper对象是通过Looper的静态方法myLooper()获取的，而myLooper()是通过调用sThreadLocal.get()来得到的，即Handler的mLooper就是当前线程的Looper对象，Handler的mQueue就是mLooper.mQueue。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mLooper = Looper.myLooper();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;if (mLooper == null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;   throw new RuntimeException(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;quot;Can&amp;apos;t create handler inside thread that has not called Looper.prepare()&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;mQueue = mLooper.mQueue;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;……&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public static Looper myLooper() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    return sThreadLocal.get();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;线程通信、ActivityThread及Thread类是理解Android线程管理的关键。&lt;/p&gt;
&lt;p&gt;线程，作为CPU调度资源的基本单位，在Android等针对嵌入式设备的操作系统中，有着非常重要和基础的作用。本小节主要从以下三个方面进行分析：&lt;/p&gt;
&lt;p&gt;《Andr
    
    </summary>
    
      <category term="Android线程管理" scheme="http://yhthu.com/categories/Android%E7%BA%BF%E7%A8%8B%E7%AE%A1%E7%90%86/"/>
    
    
      <category term="Android线程" scheme="http://yhthu.com/tags/Android%E7%BA%BF%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Android开发笔记——图片缓存、手势及OOM分析</title>
    <link href="http://yhthu.com/2016/05/03/201605036/"/>
    <id>http://yhthu.com/2016/05/03/201605036/</id>
    <published>2016-05-03T09:16:34.821Z</published>
    <updated>2016-05-03T09:16:41.279Z</updated>
    
    <content type="html">&lt;p&gt;把图片缓存、手势及OOM三个主题放在一起，是因为在Android应用开发过程中，这三个问题经常是联系在一起的。首先，预览大图需要支持手势缩放，旋转，平移等操作；其次，图片在本地需要进行缓存，避免频繁访问网络；最后，图片（Bitmap）是Android中占用内存的大户，涉及高清大图等处理时，内存占用非常大，稍不谨慎，系统就会报OOM错误。&lt;/p&gt;
&lt;p&gt;庆幸的是，这三个主题在Android开发中属于比较普遍的问题，有很多针对于此的通用的开源解决方案。因此，本文主要说明笔者在开发过程中用到的一些第三方开源库。主要内容如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Universal Image Loader、Picasso、Glide与Fresco的对比及使用&lt;/li&gt;
&lt;li&gt;PhotoView、GestureImageView的原理及使用&lt;/li&gt;
&lt;li&gt;leakcanry内存分析工具&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一、Universal-Image-Loader、Picasso、Glide与Fresco的对比及使用&quot;&gt;&lt;a href=&quot;#一、Universal-Image-Loader、Picasso、Glide与Fresco的对比及使用&quot; class=&quot;headerlink&quot; title=&quot;一、Universal Image Loader、Picasso、Glide与Fresco的对比及使用&quot;&gt;&lt;/a&gt;一、Universal Image Loader、Picasso、Glide与Fresco的对比及使用&lt;/h2&gt;&lt;p&gt;Universal Image Loader（UIL）、Picasso、Glide与Fresco是Android中进行图片加载的常用第三方库，主要封装了内存缓存、磁盘缓存、网络请求缓存、线程池等方法，抽象了图片加载的流程，很大程度避免了加载图片引起的内存溢出，提高了图片加载的效率。下图是笔者近期从各个库的github页面查询到的信息：&lt;br&gt;&lt;img src=&quot;/img/20160108-1.png&quot; alt=&quot;&quot;&gt;&lt;br&gt; 需要说明的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Imageloader是最早开源的图片缓存库，目前作者已停止维护（11.27）；&lt;/li&gt;
&lt;li&gt;Picasso的实际作者是Square的Jake Wharton，Android领域的绝对大牛；&lt;/li&gt;
&lt;li&gt;Glide是由Google员工开源的，在Google I/O 2014官方应用中推荐使用；&lt;/li&gt;
&lt;li&gt;Fresco的图片加载不使用Java堆内存，而是匿名共享内存（Ashmem）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;附上各个库的github地址：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Universal Image Loader：&lt;a href=&quot;https://github.com/nostra13/Android-Universal-Image-Loader.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/nostra13/Android-Universal-Image-Loader.git&lt;/a&gt;&lt;br&gt;Picasso：&lt;a href=&quot;https://github.com/square/picasso.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/square/picasso.git&lt;/a&gt;&lt;br&gt;Glide：&lt;a href=&quot;https://github.com/bumptech/glide.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/bumptech/glide.git&lt;/a&gt;&lt;br&gt;Fresco：&lt;a href=&quot;https://github.com/facebook/fresco.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/facebook/fresco.git&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这四个图片缓存库的基本使用（HelloWorld）都可以通过一句代码实现，分别如下：&lt;br&gt;&lt;strong&gt;UIL：&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;ImageLoader.getInstance().displayImage(url, imageView);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Picasso：&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Picasso.with(context).load(url).into(imageView);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Glide：&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Glide.with(context).load(url).into(imageView);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fresco：&lt;/strong&gt;&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;simpleDraweeView.setImageURI(uri);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;细心的朋友可以看出，Picasso和Glide的API非常类似。事实上，这四个库在实现的核心思想上都比较相似，可以抽象为以下五个模块：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;RequestManager，主要负责请求生成和管理模块；&lt;/li&gt;
&lt;li&gt;Engine，主要负责创建任务以及执行调度；&lt;/li&gt;
&lt;li&gt;GetDataInterface，获取数据的接口，主要用于从内存缓存、磁盘缓存以及网络等获取图片数据；&lt;/li&gt;
&lt;li&gt;Displayer，主要用于显示图片，可能是对ImageView的封装或者其他虚拟的Displayer；&lt;/li&gt;
&lt;li&gt;Processor，主要负责处理图片，比如图片的旋转、压缩以及截取等操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;说一句题外话，掌握了各种开源库的实现的核心思想后，会发现软件工程的一个共同点，就是通过将流程形式化、抽象化，从而提高效率。不论是业务的效率，还是开发的效率，这或许也是软件作为一门科学的核心思想。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;ImageLoader的设计及优点&lt;/strong&gt;&lt;br&gt;ImageLoader加载的流程如下图。（需要申明：下面三张流程图来自Trinea，尊重原作者版权）&lt;br&gt;&lt;img src=&quot;/img/20160108-2.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;ImageLoader收到加载及显示图片的任务，ImageLoaderEngine分发任务，获得图片数据后，BitmapDisplayer 在ImageAware中显示。&lt;br&gt;&lt;strong&gt;ImageLoader的优点：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;支持下载进度监听&lt;/li&gt;
&lt;li&gt;可以在 View 滚动中暂停图片加载，通过 PauseOnScrollListener 接口可以在 View 滚动中暂停图片加载。&lt;/li&gt;
&lt;li&gt;默认实现多种内存缓存算法，这几个图片缓存都可以配置缓存算法，不过 ImageLoader 默认实现了较多缓存算法，如 Size 最大先删除、使用最少先删除、最近最少使用、先进先删除、时间最长先删除等。&lt;/li&gt;
&lt;li&gt;支持本地缓存文件名规则定义&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Picasso的设计及优点&lt;/strong&gt;&lt;br&gt;Picasso的加载流程如下图：&lt;br&gt;&lt;img src=&quot;/img/20160108-3.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;Picasso收到加载及显示图片的任务，Dispatcher 负责分发和处理，通过MemoryCache及Handler获取图片，通过PicassoDrawable显示到Target中。&lt;br&gt;&lt;strong&gt;Picasso的优点：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;自带统计监控功能，支持图片缓存使用的监控，包括缓存命中率、已使用内存大小、节省的流量等。&lt;/li&gt;
&lt;li&gt;支持优先级处理，每次任务调度前会选择优先级高的任务，比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。&lt;/li&gt;
&lt;li&gt;支持延迟到图片尺寸计算完成加载，支持飞行模式、并发线程数根据网络类型而变，手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数，比如 wifi 最大并发为 4， 4g 为 3，3g 为 2。这里 Picasso 根据网络类型来决定最大并发数，而不是 CPU 核数。&lt;/li&gt;
&lt;li&gt;“无”本地缓存，不是说没有本地缓存，而是 Picasso 自己没有实现，交给了 Square 的另外一个网络库 okhttp 去实现，这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glide的设计及优点&lt;/strong&gt;&lt;br&gt;Glide的加载流程如下图：&lt;br&gt;&lt;img src=&quot;/img/20160108-4.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;Glide 收到加载及显示资源的任务，Engine 处理请求，通过Fetcher获取数据，经Transformation 处理后交给Target显示。&lt;br&gt;&lt;strong&gt;Glide的优点：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(1) 图片缓存-&amp;gt;媒体缓存&lt;br&gt;Glide 不仅是一个图片缓存，它支持 Gif、WebP、缩略图。甚至是 Video，所以更该当做一个媒体缓存。&lt;br&gt;(2) 支持优先级处理&lt;br&gt;(3) 与 Activity/Fragment 生命周期一致，支持 trimMemory&lt;br&gt;Glide 对每个 context 都保持一个 RequestManager，通过 FragmentTransaction 保持与 Activity/Fragment 生命周期一致，并且有对应的 trimMemory 接口实现可供调用。&lt;br&gt;(4) 支持 okhttp、Volley&lt;br&gt;Glide 默认通过 UrlConnection 获取数据，可以配合 okhttp 或是 Volley 使用。实际 ImageLoader、Picasso 也都支持 okhttp、Volley。&lt;br&gt;(5) 内存友好&lt;br&gt;① Glide 的内存缓存有个 active 的设计&lt;br&gt;从内存缓存中取数据时，不像一般的实现用 get，而是用 remove，再将这个缓存数据放到一个 value 为软引用的 activeResources map 中，并计数引用数，在图片加载完成后进行判断，如果引用计数为空则回收掉。&lt;br&gt;② 内存缓存更小图片&lt;br&gt;Glide 以 url、viewwidth、viewheight、屏幕的分辨率等做为联合 key，将处理后的图片缓存在内存缓存中，而不是原始图片以节省大小&lt;br&gt;③ 与 Activity/Fragment 生命周期一致，支持 trimMemory&lt;br&gt;④ 图片默认使用默认 RGB565 而不是 ARGB888&lt;br&gt;虽然清晰度差些，但图片更小，也可配置到 ARGB_888。&lt;br&gt;其他：Glide 可以通过 signature 或不使用本地缓存支持 url 过期&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;关于Fresco&lt;/strong&gt;&lt;br&gt;Fresco库开源较晚，目前还没有正式的1.0版本。但其功能比前三个库都强大，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图片存储系统匿名共享内存Ashmem（Anonymous Shared Memory），并不分配Java堆内存，因此图片加载不会引起堆内存抖动；&lt;/li&gt;
&lt;li&gt;JPEG图像流加载（先显示图像轮廓，再慢慢加载清晰图像）；&lt;/li&gt;
&lt;li&gt;更加完善的图像处理、显示方式；&lt;/li&gt;
&lt;li&gt;JPEG图像本地（native）变换尺寸，避免OOM；&lt;/li&gt;
&lt;li&gt;……&lt;br&gt;关于系统匿名共享内存Ashmem，会在后续的一篇关于Android的内存使用的文章中详述，这里仅作简单介绍：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在Android系统里面，Ashmem这个区域的内存并不属于Java Heap，也不属于Native Heap。当Ashmem中的某个内存空间像要被释放时候，会通过系统调用unpin来告知。但实际上这块内存空间的数据并没有被真正的擦除。如果Android系统发现内存吃紧时，就会把unpin的内存空间利用起来去存储所需的数据。而被unpin的内存空间，是可以被重新pin的，如果此时的该内存空间还没有被其他人使用的话，就节省了重新往Ashmem重新写入数据的过程了。所以，Ashmem这个工作原理是一种延迟释放。&lt;/p&gt;
&lt;p&gt;另外，学习Ashmem可以参考罗升阳大师的博客：&lt;/p&gt;
&lt;p&gt;Android系统匿名共享内存Ashmem（Anonymous Shared Memory）简要介绍和学习计划&lt;br&gt;Android系统匿名共享内存Ashmem（Anonymous Shared Memory）驱动程序源代码分析&lt;br&gt;Android系统匿名共享内存Ashmem（Anonymous Shared Memory）在进程间共享的原理分析&lt;/p&gt;
&lt;h2 id=&quot;二、PhotoView、GestureImageView的原理及使用&quot;&gt;&lt;a href=&quot;#二、PhotoView、GestureImageView的原理及使用&quot; class=&quot;headerlink&quot; title=&quot;二、PhotoView、GestureImageView的原理及使用&quot;&gt;&lt;/a&gt;二、PhotoView、GestureImageView的原理及使用&lt;/h2&gt;&lt;p&gt;需要使用上述第三方开源库进图片加载的一个典型场景是点击查看大图。大图支持手势缩放、旋转、平移等操作，ImageView的手势缩放，有很多种方法，绝大多数开源自定义缩放都是修改了ondraw函数来实现的。但是ImageView本身有scaleType属性，通过设置android:scaleType=”matrix” 可以轻松实现缩放功能。缩放的优点是实现起来简单，同时因为没有反复调用ondraw函数，缩放过程中不会有闪烁现象。另外，需要注意的是，scaleType控制图片的缩放方式，该图片指的是资源而不是背景，换句话说，android:src=”@drawable/ic_launcher”，而非android:background=”@drawable/ic_launcher”。&lt;/p&gt;
&lt;p&gt;在github上可以找到很多开源的实现，这里主要举两个例子进行简单说明。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PhotoView地址：&lt;a href=&quot;https://github.com/bm-x/PhotoView.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/bm-x/PhotoView.git&lt;/a&gt;&lt;br&gt;GestureImageView地址：&lt;a href=&quot;https://github.com/jasonpolites/gesture-imageview.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/jasonpolites/gesture-imageview.git&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PhotoView的介绍：&lt;/p&gt;
&lt;p&gt;1.Gradle添加依赖（推荐）&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;dependencies &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    compile &amp;apos;com.bm.photoview:library:1.3.6&amp;apos;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;（或者也可以将项目下载下来，将Info.java和PhotoView.java两个文件拷贝到你的项目中，不推荐）——这种方式适用于Eclipse。&lt;/p&gt;
&lt;p&gt;2.xml添加&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;com.bm.library.PhotoView&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     android:id=&amp;quot;@+id/img&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     android:layout_width=&amp;quot;match_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     android:layout_height=&amp;quot;match_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     android:scaleType=&amp;quot;centerInside&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;     android:src=&amp;quot;@drawable/bitmap1&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;3.java代码&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;PhotoView photoView = (PhotoView) findViewById(R.id.img);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 启用图片缩放功能&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;photoView.enable();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 禁用图片缩放功能 (默认为禁用，会跟普通的ImageView一样，缩放功能需手动调用enable()启用)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;photoView.disenable();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 获取图片信息&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;Info info = photoView.getInfo();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 从一张图片信息变化到现在的图片，用于图片点击后放大浏览，具体使用可以参照demo的使用&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;photoView.animaFrom(info);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 从现在的图片变化到所给定的图片信息，用于图片放大后点击缩小到原来的位置，具体使用可以参照demo的使用&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;photoView.animaTo(info,new Runnable() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       public void run() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;           //动画完成监听&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;       &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;   &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// 获取动画持续时间&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;int d = PhotoView.getDefaultAnimaDuring();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;PhotoView实现的基本原理是在继承于ImageView的PhotoView中采用了缩放Matrix及手势监听。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class PhotoView extends ImageView &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Matrix mBaseMatrix = new Matrix();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Matrix mAnimaMatrix = new Matrix();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Matrix mSynthesisMatrix = new Matrix();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private Matrix mTmpMatrix = new Matrix();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private RotateGestureDetector mRotateDetector;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private GestureDetector mDetector;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private ScaleGestureDetector mScaleDetector;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private OnClickListener mClickListener;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    private ScaleType mScaleType;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    ……&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;PhotoView的实现与上述原理基本一致，这里不再赘述。对于自定义控件的实现，后续文章会进行详细的分析。&lt;/p&gt;
&lt;p&gt;GestureImageView的简介如下：&lt;br&gt;1.Configured as View in layout.xml&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;LinearLayout&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    xmlns:gesture-image=&amp;quot;http://schemas.polites.com/android&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:layout_width=&amp;quot;fill_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:layout_height=&amp;quot;fill_parent&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;lt;com.polites.android.GestureImageView&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        android:id=&amp;quot;@+id/image&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        android:layout_width=&amp;quot;fill_parent&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        android:layout_height=&amp;quot;wrap_content&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        android:src=&amp;quot;@drawable/image&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        gesture-image:min-scale=&amp;quot;0.1&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        gesture-image:max-scale=&amp;quot;10.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        gesture-image:strict=&amp;quot;false&amp;quot;/&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;/LinearLayout&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;2.Configured Programmatically&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class SampleActivity extends Activity &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    @Override&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    public void onCreate(Bundle savedInstanceState) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        super.onCreate(savedInstanceState);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        setContentView(R.layout.main);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        GestureImageView view = new GestureImageView(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        view.setImageResource(R.drawable.image);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        view.setLayoutParams(params);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        ViewGroup layout = (ViewGroup) findViewById(R.id.layout);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        layout.addView(view);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;原理基本同PhotoView一致，不再赘述。&lt;/p&gt;
&lt;h2 id=&quot;三、OOM分析工具——LeakCanary&quot;&gt;&lt;a href=&quot;#三、OOM分析工具——LeakCanary&quot; class=&quot;headerlink&quot; title=&quot;三、OOM分析工具——LeakCanary&quot;&gt;&lt;/a&gt;三、OOM分析工具——LeakCanary&lt;/h2&gt;&lt;p&gt; LeakCanary的介绍：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A memory leak detection library for Android and Java.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可见，LeakCanary主要用于检测各种内存不能被GC，从而导致泄露的情况。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LeakCanary的地址   &lt;a href=&quot;https://github.com/square/leakcanary.git&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/square/leakcanary.git&lt;/a&gt;&lt;br&gt;Demo地址：&lt;br&gt;&lt;a href=&quot;https://github.com/liaohuqiu/leakcanary-demo.git（AS）&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/liaohuqiu/leakcanary-demo.git（AS）&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/teffy/LeakcanarySample-Eclipse.git（Eclipse）&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/teffy/LeakcanarySample-Eclipse.git（Eclipse）&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下面是demo中TestActivity中的TextView被静态变量引用导致无法回收引起的内存泄露的截图。&lt;br&gt;&lt;img src=&quot;/img/20160108-5.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;LeakCanary的使用较为简单，首先添加依赖工程：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;dependencies &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;   debugCompile &amp;apos;com.squareup.leakcanary:leakcanary-android:1.3&amp;apos;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;   releaseCompile &amp;apos;com.squareup.leakcanary:leakcanary-android-no-op:1.3&amp;apos;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;其次，在application的onCreate()方法中进行初始化。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public class ExampleApplication extends Application &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  @Override public void onCreate() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super.onCreate();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    LeakCanary.install(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;经过这两步之后就可以使用了。LeakCanary.install(this)会返回一个预定义的 RefWatcher，同时也会启用一个ActivityRefWatcher，用于自动监控调用 Activity.onDestroy() 之后泄露的 activity。如果需要监听fragment，则在fragment的onDestroy()方法进行注册：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;public abstract class BaseFragment extends Fragment &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  @Override public void onDestroy() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    super.onDestroy();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    refWatcher.watch(this);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;当然，需要对某个变量进行监听，直接对其进行watch即可。&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;RefWatcher refWatcher = &amp;#123;...&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;// We expect schrodingerCat to be gone soon (or not), let&amp;apos;s watch it.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;refWatcher.watch(schrodingerCat);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;需要注意的是，在eclipse中使用LeakCanary需要在AndroidManifest文件中对堆占用分析以及展示的Service进行申明：&lt;br&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;service&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:name=&amp;quot;com.squareup.leakcanary.internal.HeapAnalyzerService&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:enabled=&amp;quot;false&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:process=&amp;quot;:leakcanary&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;service&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:name=&amp;quot;com.squareup.leakcanary.DisplayLeakService&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:enabled=&amp;quot;false&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;activity&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:name=&amp;quot;com.squareup.leakcanary.internal.DisplayLeakActivity&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:enabled=&amp;quot;false&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:icon=&amp;quot;@drawable/leak_canary_icon&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:label=&amp;quot;@string/leak_canary_display_activity_label&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:taskAffinity=&amp;quot;com.squareup.leakcanary&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    android:theme=&amp;quot;@style/leak_canary_LeakCanary.Base&amp;quot; &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;lt;intent-filter&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;lt;action android:name=&amp;quot;android.intent.action.MAIN&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &amp;lt;category android:name=&amp;quot;android.intent.category.LAUNCHER&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;lt;/intent-filter&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;注意：HeapAnalyzerService采用了多进程android:process=”:leakcanary”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上述开源工具的使用都较为简单，关于详细使用，请参考其github地址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;四、一些杂乱的总结&quot;&gt;&lt;a href=&quot;#四、一些杂乱的总结&quot; class=&quot;headerlink&quot; title=&quot;四、一些杂乱的总结&quot;&gt;&lt;/a&gt;四、一些杂乱的总结&lt;/h2&gt;&lt;p&gt; &lt;strong&gt;内存泄露的常见原因：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;静态对象：监听器，广播，webview；&lt;/li&gt;
&lt;li&gt;this$0：线程，定时器，Handler；&lt;/li&gt;
&lt;li&gt;系统：TextLine，输入法，音频&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;兜底回收内存：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Activity泄露会导致该Activity引用的Bitmap/DrawingCache等无法释放，兜底回收是指对已泄露的Activity，尝试回收其持有的资源。在onDestroy中从rootview开始，递归释放所有子VIew涉及的图片，背景，DrawingCache，监听器等资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;降低Runtime内存的方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;减少bitmap占用的内存：1）防止bitmap占用资源过大，2.x系统打开BitmapFactory.Options中的inNativeAlloc；4.x系统采用Facebook的fresco库，将图片资源放于native中。2）图片按需加载，图片的大小不应超过view的大小。3）统一的bitmap加载器：Picasso/Fresco。4）图片存在像素浪费。&lt;/li&gt;
&lt;li&gt;自身内存占用监控：1）实现原理：通过Runtime获取maxMemory，而totalMemory-freeMemory即为当前真正使用的dalvik内存。2）操作方式：定期检查这个值，达到80%就去释放各种cache资源（bitmap的cache）。&lt;/li&gt;
&lt;li&gt;使用多进程。对于webview，图库等，由于存在内存系统泄露，可以采用单独的进程。&lt;/li&gt;
&lt;/ol&gt;
</content>
    
    <summary type="html">
    
      &lt;p&gt;把图片缓存、手势及OOM三个主题放在一起，是因为在Android应用开发过程中，这三个问题经常是联系在一起的。首先，预览大图需要支持手势缩放，旋转，平移等操作；其次，图片在本地需要进行缓存，避免频繁访问网络；最后，图片（Bitmap）是Android中占用内存的大户，涉及高
    
    </summary>
    
      <category term="Android开发笔记" scheme="http://yhthu.com/categories/Android%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="图片缓存" scheme="http://yhthu.com/tags/%E5%9B%BE%E7%89%87%E7%BC%93%E5%AD%98/"/>
    
      <category term="Android" scheme="http://yhthu.com/tags/Android/"/>
    
      <category term="OOM分析" scheme="http://yhthu.com/tags/OOM%E5%88%86%E6%9E%90/"/>
    
  </entry>
  
</feed>
