<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>EricChows</title>
  
  <subtitle>Android Developer</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yoursite.com/"/>
  <updated>2018-09-06T09:48:13.820Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name>Eric Chows</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>★★★★★博客导航★★★★★</title>
    <link href="http://yoursite.com/Myblog/"/>
    <id>http://yoursite.com/Myblog/</id>
    <published>2098-12-31T16:00:00.000Z</published>
    <updated>2018-09-06T09:48:13.820Z</updated>
    
    <content type="html"><![CDATA[<h1 id="基础知识"><a href="#基础知识" class="headerlink" title="基础知识"></a>基础知识</h1><ol><li><a href="../Java-Base">Java</a> Ongoing，</li><li><a href="../Android-Base">Android</a> Ongoing</li><li><a href="../C++-Base">C++</a>  Ongoing</li><li><a href="../Linux-Base">Linux</a> Ongoing</li></ol><h1 id="Framework源码解析"><a href="#Framework源码解析" class="headerlink" title="Framework源码解析"></a>Framework源码解析</h1><ol><li><a href="../Android-PackageManagerService-Analysis">Android中PackageManagerService机制分析</a>    进行到了30%，还未完成 </li><li><a href="../Android-StrictMode-Analysis">StrictMode 机制以及性能调优</a></li><li><a href="../Android-ANR-Analysis">ANR 机制以及问题分析</a></li><li><a href="../Android-Watchdog-Analysis">Watchdog机制以及问题分析</a></li><li><a href="../Android-Window-Mechanism">Android中Window机制</a></li><li><a href="../Android-Log-Analysis">Android的Log机制分析</a></li><li><a href="../Android-Launcher-Activity">Android Launcher 启动Activity工作流程</a> Ongoing</li><li><a href="../Android-AIDL-Mechanism">Andorid中AIDL解析</a> Ongoing</li><li><a href="../Android-ActivityManagerService-Analysis">Android中ActivityManagerService解析</a> 待完成</li><li><a href="../Android-Make">Android 编译</a></li><li><a href="../Android-Performance">Android系统性能调优工具</a></li><li><a href="../Android-Digital-Signature">Android数字签名</a></li></ol><h1 id="待完成"><a href="#待完成" class="headerlink" title="待完成"></a>待完成</h1><ol><li><a href="../Android-Activity-Analysis">Android中Activity解析</a></li><li><a href="../Android-Ashmem-Analysis">Android匿名共享内存Ashmem解析</a></li><li><a href="../Android-Binder-Analysis">Android中Binder机制解析</a></li><li><a href="../Android-Boot-Process">Android中启动过程解析</a></li><li><a href="../Android-BroadcastRecevier-Analysis">Android中BroadcastRecevier解析</a></li><li><a href="../Android-ContentProvider-Analysis">Android中ContentProvider解析</a></li><li><a href="../Android-EventBus">Android中EventBus解析</a></li><li><a href="../Android-Handler-Analysis">Android中Handler解析</a></li><li><a href="../Android-JNI-NDK">Android中JNI与NDK</a></li><li><a href="../Android-KeyEvent-Analysis">Android中按键事件传递分析</a></li><li><a href="../Android-MVC-MVP">Android中MVC模式和MVP模式</a></li><li><a href="../Android-Process-Analysis">Android中Process</a></li><li><a href="../Android-Service-Analysis">Android中Service解析</a></li><li><a href="../Android-SurfaceFlinger-Analysis">Android中SurfaceFlinger解析</a></li><li><a href="../Android-WindowManagerService-Analysis">Android中WindowManagerService解析</a></li><li><a href="../Java-VirtualMachine">Java虚拟机</a></li></ol><h1 id="Application源码解析"><a href="#Application源码解析" class="headerlink" title="Application源码解析"></a>Application源码解析</h1><ol><li><a href="../Android-Keyguard-Analysis">Android中Keyguard解析</a> 还未完成，需要继续补充</li><li><a href="../Android-SystemUI-Analysis">Android中SystemUI解析</a> 还未完成，需要继续补充</li><li><a href="../Android-Launcher-Analysis">Android中Launcher解析</a> 还未完成，需要继续补充</li></ol><h1 id="疑难杂症"><a href="#疑难杂症" class="headerlink" title="疑难杂症"></a>疑难杂症</h1><ol><li><a href="../Android8.0-charging-icon-cannot-show">Android8.0充电图标不显示</a></li></ol><h1 id="工具使用"><a href="#工具使用" class="headerlink" title="工具使用"></a>工具使用</h1><ol><li><a href="../ADB-Android-Debug-Bridge">ADB (Android Debug Bridge)常用命令</a></li><li><a href="../Android-Performance">Android系统性能调优工具</a></li><li><a href="../Git+Repo">Git and Repo</a>  待完成</li></ol><h1 id="试题"><a href="#试题" class="headerlink" title="试题"></a>试题</h1><ol><li><a href="../Android-InterView">Android面试汇总</a>  待完成</li></ol><p><a href="../Android-Article">看过或者需要重新看的文章</a></p><h1 id="新技术探索"><a href="#新技术探索" class="headerlink" title="新技术探索"></a>新技术探索</h1><ol><li><a href="../Block-Chain">区块链开发</a></li><li><a href="../Chrome">Chrome插件开发</a></li><li><a href="../Fuchsia">Fuchsia</a></li><li><a href="../Python">Python开发</a></li><li>[Flutter]</li><li>[Go]</li><li>[Dart]</li><li><a href="../Kotlin">Kotlin</a></li><li>[Firebase]</li><li>[Tensorflow]</li></ol><h1 id="Kaios"><a href="#Kaios" class="headerlink" title="Kaios"></a>Kaios</h1><p><a href="../Kaios">Kaios</a></p><p><a href="../AliOS">AliOS/YunOS</a></p><h1 id="已经整理完成的文章"><a href="#已经整理完成的文章" class="headerlink" title="已经整理完成的文章"></a>已经整理完成的文章</h1><ol><li><a href="../ADB-Android-Debug-Bridge">ADB (Android Debug Bridge)常用命令</a><br>需要做的延伸是：分析adb 的源码</li><li><p><a href="../Window-Bat">windows批处理 BAT技巧</a><br>需要延伸的是： 用bat脚本抓所有的Android log<br>在Ubuntu环境下，使用其他脚本抓所有的Android log</p></li><li><p><a href="../各类工具使用小技巧">各类工具使用小技巧</a><br>需要延伸的是： 日常工作中使用过的工具的小技巧需要随时收录进来</p></li><li><p><a href="../问题锦集-速查">问题锦集-速查</a><br>需要延伸的是： 日常工作中遇到的常见的一些问题，最好都记录下来，保存到这里来</p></li><li><p><a href="../Android-Make">Android 编译</a><br>需要延伸的是： Android编译流程 和 编译过程中各个文件的作用</p></li><li><p><a href="../Linux-Command">Linux脚本编写基础</a><br>是一篇学习的文章： 还没有学习完，文章格式乱七八糟，继续维护</p></li><li><p><a href="../Android开发如何做内存优化">android 开发如何做内存优化</a></p></li><li><p><a href="../Android-View-Mechanism">Android 编程下 Touch 事件的分发和消费机制</a><br>需要延伸的是：案例</p></li></ol><p>常用图标：<br>└──<br>•<br>★</p><h1 id="语言手册"><a href="#语言手册" class="headerlink" title="语言手册"></a>语言手册</h1><p><a href="https://mirrors.segmentfault.com/" target="_blank" rel="noopener">https://mirrors.segmentfault.com/</a><br>Introduction to Tornado 中文翻译<br>Android 设计指南简体中文版<br>Swift语言指南<br>PHP 中文手册<br>Node.js 手册<br>Golang 手册<br>Python 2.x 手册<br>Python 3.x 手册<br>Laravel 手册<br>Rust 手册<br>React 手册<br>Kotlin 手册</p><h1 id="IT"><a href="#IT" class="headerlink" title="IT"></a>IT</h1><p><a href="../IT-Base">IT学习</a></p><h1 id="对技术的建议"><a href="#对技术的建议" class="headerlink" title="对技术的建议"></a>对技术的建议</h1><ol><li><a href="../IT-Advice-1">给技术人员一些技术以外的建议</a></li></ol><h1 id="生活"><a href="#生活" class="headerlink" title="生活"></a>生活</h1><ol><li><a href="../Life-Advice">关于工作和成长的121条具体建议</a></li></ol><h1 id="English"><a href="#English" class="headerlink" title="English"></a>English</h1><ol><li><a href="../L-My-Approach-to-Getting-Dramatically-Better-as-a-Programmer">My Approach to Getting Dramatically Better as a Programmer</a></li></ol><!--Welcome to [Hexo](https://hexo.io/)! This is your very first post. Check [documentation](https://hexo.io/docs/) for more info. If you get any problems when using Hexo, you can find the answer in [troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me on [GitHub](https://github.com/hexojs/hexo/issues).## Quick Start### Create a new post<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html">Deployment</a><br>–&gt;</p>-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;基础知识&quot;&gt;&lt;a href=&quot;#基础知识&quot; class=&quot;headerlink&quot; title=&quot;基础知识&quot;&gt;&lt;/a&gt;基础知识&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;../Java-Base&quot;&gt;Java&lt;/a&gt; Ongoing，&lt;/li&gt;
&lt;li&gt;&lt;a hre
      
    
    </summary>
    
      <category term="All" scheme="http://yoursite.com/categories/All/"/>
    
    
      <category term="Myblog" scheme="http://yoursite.com/tags/Myblog/"/>
    
  </entry>
  
  <entry>
    <title>Android中PackageManagerService机制分析</title>
    <link href="http://yoursite.com/Android-PackageManagerService-Analysis/"/>
    <id>http://yoursite.com/Android-PackageManagerService-Analysis/</id>
    <published>2018-05-31T14:13:56.000Z</published>
    <updated>2018-07-16T13:37:45.431Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h1><p>每一个社会群落都有管理机制，其中有三个要素：被管理者、管理者以及管理机制的运转。在Android的世界中，有一处群落叫“包管理”，要研究Android的包管理机制，同样可以从以下几个角度来思考：</p><ol><li>被管理的对象是什么？</li><li>管理者的职能是什么？</li><li>管理机制是如何运转的？<br>所谓包，其实就是一种文件格式，譬如APK包、JAR包等。在Android中存活着很多包，所有的应用程序都是APK包，很多构成Android运行环境的都是JAR包，还有一些以so为后缀的库文件，包管理者很重要的一个职能就是识别不同的包，统一维护这些包的信息。当有一个包进入或离开Android世界，都需要向包管理者申报，其他管理部门要获取包的具体信息，也都需要向包管理者申请。</li></ol><p>如同社会是由人与人的协作形成，不同的包之间也需要进行协作。既然有协作，自然就有协作的规范，一个包可以干什么，不可以干什么，都需要有一个明确的范围界定，这就是包管理中的权限设计。涉及到的内容非常广泛，Linux的UGO（User Group Other）和ACL（Access Control List，访问控制列表）权限管理、数字签名与验证、Android授权机制、Selinux，都是包管理中权限设计的组成部分。</p><p>Android的世界就如同一个井然有序的人类社会，除了包管理部门，还有其他各种管理部门，譬如电源管理、窗口管理、活动管理等等，大家不仅各司其职，而且也有交流往来。从APK的安装到Activity的显示这么一个看似简单的过程，却需要大量管理部门参与进来，不断地进行数据解析、封装、传递、呈现，内部机理十分复杂。</p><blockquote><p>PackageManagerService是包管理中最重要的服务，为了描述方便，本文会简写成PMS。<br>PMS的部分函数带有LI后缀，表示需要获取mInstalllock这个锁时才能执行；部分函数带有LP后缀，表示需要获取mPackages这个锁才能执行。</p></blockquote><p>PackageManagerService相关的代码路径如下：<br><a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/" target="_blank" rel="noopener">frameworks/base/core/java/android/content/pm/</a><br><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/" target="_blank" rel="noopener">frameworks/base/services/core/java/com/android/server/pm</a></p><h1 id="被管理对象的形态"><a href="#被管理对象的形态" class="headerlink" title="被管理对象的形态"></a>被管理对象的形态</h1><p>Android中的APK和JAR包都以静态文件的形式分布在不同的硬件分区，包管理者面临的第一个任务就是将这些静态的文件转化成内存的数据结构，这样才能将其管理起来。Android中最重要的包管理对象就是APK，APK可以包含so文件，负责将静态文件转换内存中数据结构的工具就是PackageParser，包解析器。<br><img src="/Android-PackageManagerService-Analysis/1-packagemanager-package-from-static-to-dynamic.png" alt="PackageParser"></p><p>Android L(5.0)以后，支持APK拆分，即一个APK可以分割成很多部分，位于相同的目录下，每一个部分都是一个单独的APK文件，所有的APK文件具备相同的签名，在APK解析过程中，会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK，Android称其为Monolithic；对于拆分后的APK，Android称其为Cluster。</p><blockquote><p>在Android L(5.0)以前，APK文件都是直接位于app或priv-app目录下，譬如短彩信APK的目录就是/system/priv-app/Mms.apk；到了Android L(5.0)之后，多了一级目录结构，譬如短彩信APK的目录是/system/priv-app/Mms/Mms.apk，这是Android为了支持APK拆分而做的改动，如果要将短彩信APK进行拆分，那所有被拆出来的APK都位于/system/priv-app/Mms/即可，这样在包解析时，就会变成以Cluster的方式解析目录。</p></blockquote><p>一个包在内存中的数据结构就是Package，那么，Package有一些什么属性?是怎么从APK文件中获取数据的呢？ 这就涉及到包解析器的工作原理。</p><h1 id="包解析器"><a href="#包解析器" class="headerlink" title="包解析器"></a>包解析器</h1><p>为了先让读者对被管理对象有一个初步的认识，我们先把一个包最终在内存中的数据结构拎出来。其实生成这个数据结构，需要包管理者进行大量的调度工作，调度中心是PMS，包解析的过程也都是由PMS驱动的。在分析包解析过程之前，我们先上包解析的结果：<br><img src="/Android-PackageManagerService-Analysis/2-packagemanager-packageparser.png" alt="包解析结果"><br>这个类图，示意了一个包最终在内存中的数据结构Package，它包含很多属性，部分属性还是包解析器中的子数据结构。我们可以从设计的角度来理解这个类图：</p><ul><li><p>一个包中有很多组件，为此设计了一个高层的基类Component，所有具体的组件都是Component的子类。什么是组件呢？AndroidManifest.xml文件中所定义的的一些标签，就是组件，譬如<activity>，<service>，<provider>，<permission>等，这些标签分别对应到包解析器中的一个数据结构，它们各自有自身的属性。</permission></provider></service></activity></p></li><li><p>诸如<activity>，<service>标签，都可以配置<intent-filter>，来过滤其可以接收的Intent，这些信息也需要在包解析器中体现出来，为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同，所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式，通过泛型的手段实现。</intent-filter></service></activity></p></li><li><p>各种组件最终聚合到Package这个数据结构中，形成了最终包解析器的输出。当然，在解析的过程中，还有利用了一些数据结构来优化设计，PackageLite和ApkLite就是一些很简单的数据封装。</p></li></ul><p>要得到以上的数据结构，包解析器<a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/PackageParser.java" target="_blank" rel="noopener">PackageParser</a>功不可没，从接收一个静态的文件(File类型)开始，会经过一个漫长的包解析过程，直到生成最终的Package：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">parsePackages(File file...)</span><br><span class="line">└── parseClusterPackage(File packageDir...)</span><br><span class="line">    └── parseClusterPackageLite(File packageDir...)</span><br><span class="line">    |   └── parseApkLite(File apkFile...)</span><br><span class="line">    |       └── parseApkLite(String codePath...)</span><br><span class="line">    └── parseBaseApk()</span><br><span class="line">        └── parseBaseApplication()</span><br><span class="line">        |    └── parseActivity()</span><br><span class="line">        |    └── parseService()</span><br><span class="line">        |    └── ...</span><br><span class="line">        └── parseInstrumentation()</span><br><span class="line">        └── ...</span><br></pre></td></tr></table></figure></p><p>这些函数的具体逻辑本文不予分析，仅把关键的流程捋出来:</p><ol><li><p>PackageParser.parsePackages()是包解析器的入口函数，它首先会判定给定的输入是否为一个目录，如果是目录，则以为着目录下可能存在多个拆分后的APK，这就需要以Cluster的方式进行解析；如果仅仅是一个APK文件，就以Monolithic的方式解析；</p></li><li><p>解析APK，需要先得到一个中间数据结构PacakgeLite，包名、版本、拆分包等信息都会保存在这个数据结构中；由于一个包可能有多个拆分的APK，所以PackageLite可能关联到多个APK，每一个APK都对应到ApkLite这个数据结构，也是一些基本信息的封装。之所以以Lite为后缀命名，是因为这两个数据结构都比较轻量，只保存APK中很少信息；</p></li><li><p>一个APK真正的信息都写在AndroidManifest.xml这个文件中，PackageParser.parseBaseApk()这个函数就是用来解析该文件。其解析过程与AndroidManifest.xml的文件结构一一对应，譬如先解析<application>标签的内容，然后解析其下的<activity>,<service>等标签。由于AndroidManifest.xml文件的结构非常复杂，所以该函数逻辑也非常庞大，读者们可以自行分析源码。<br>至此，包解析器PackageParser就将一个静态的文件，转换成了内存中的数据结构Package，它包含了一个包的所有信息，如包名、包路径、权限、四大组件等，其数据来源主要就是AndroidManifest.xml文件。</service></activity></application></p></li></ol><h1 id="包信息体"><a href="#包信息体" class="headerlink" title="包信息体"></a>包信息体</h1><p>包解析器从静态文件中获取的数据，很多都是需要用于跨进程传递的，譬如初次启动Activity时，就需要把包信息从系统进程传递到应用进程，先完成应用进程的启动。在包解析器的类图中，我们看到Activity、Service、Provider、Permission、Instrumentaion这些类都有一个共同的特征：都具备info这个属性，其实这些类的结构非常简单，就是对info的一次封装，info这个结构体才是真正的包数据，笔者暂且称之为“包信息体”:<br><img src="/Android-PackageManagerService-Analysis/3-packagemanager-packageparser-parcelable.png" alt="包信息体"><br>所有的info都实现了Parcelable接口，意图很明显，info是可以进行跨进程传递的。不同组件的info类型是不同的，除了实现了Parcelable接口，它们之间又构成了一个庞大的数据结构，把这些具体的info类型展开，就是以下的类图：<br><img src="/Android-PackageManagerService-Analysis/4-packagemanager-packageparser-parcelable.png" alt="Info展开图"><br>可以看到，这个类图与PackageParser中的类图在结构上很相似，我们依旧是从设计的角度来理解这个类图：</p><ul><li>PackageItemInfo作为包每一项信息的高层基类：<ul><li>针对permission，permission，instrumentation等，分别为其设计了一个类，都继承自PackageItemInfo</li><li>针对activity，service，provider等四大组件，在PackageItemInfo之下又多设计了一层：ComponentInfo作为四大组件的基类</li></ul></li><li>ApplicationInfo也是包信息中的一项，但与四大组件紧密相连，四大组件肯定都属于某个Application，所以ComponentInfo与Application存在依赖关系，继而，具体到每个组件都与Application存在依赖关系</li><li>所有的包信息都聚合到PackageInfo这个类中，PackageInfo就是一个包向外提供的所有信息。其实除了上图列出来的类，还有一些类没有示意出来，譬如ConfigurationInfo，FeatureInfo，它们都可以对应到AndroidManifest.xml中的标签。<br>这些结构体中的数据，都是在包解析器时初始化的，譬如Activity依赖于ActivityInfo，在解析Activity时，就会创建一个ActivityInfo对象，把<activity>所定义的数据全都填充到ActivityInfo中。读者可以思考一下PackageParser中的Activity与此处的ActivityInfo的分开设计的目的和好处是什么？<blockquote><p>在分析包的形态时，我们见到了很多类，类的命名方式还有点相似，初读代码的时候，很容易陷入各个类之间复杂的关系网之中。不得不说，包在内存中的数据结构是比较庞大的，因为它蕴含的信息大多了。</p></blockquote></activity></li></ul><h1 id="PMS的启动过程"><a href="#PMS的启动过程" class="headerlink" title="PMS的启动过程"></a>PMS的启动过程</h1><p><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java" target="_blank" rel="noopener">frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java</a></p><h2 id="对象创建"><a href="#对象创建" class="headerlink" title="对象创建"></a>对象创建</h2><h3 id="PMS部分属性初始化"><a href="#PMS部分属性初始化" class="headerlink" title="PMS部分属性初始化"></a>PMS部分属性初始化</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码片段1：PMS部分属性的初始化</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">PackageManagerService</span><span class="params">(Context context, Installer installer,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">boolean</span> factoryTest, <span class="keyword">boolean</span> onlyCore)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 关键的Event日志，PMS开始启动了</span></span><br><span class="line">    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,</span><br><span class="line">            SystemClock.uptimeMillis());</span><br><span class="line"></span><br><span class="line">    mContext = context;</span><br><span class="line">    mFactoryTest = factoryTest;</span><br><span class="line">    mOnlyCore = onlyCore;</span><br><span class="line">    <span class="comment">// 如果是eng版，则延迟做DexOpt</span></span><br><span class="line">    mLazyDexOpt = <span class="string">"eng"</span>.equals(SystemProperties.get(<span class="string">"ro.build.type"</span>));</span><br><span class="line">    mMetrics = <span class="keyword">new</span> DisplayMetrics();</span><br><span class="line">    <span class="comment">// Settings是包管理中一个很重要的数据结构，用于维护所有包的信息</span></span><br><span class="line">    mSettings = <span class="keyword">new</span> Settings(mPackages);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.system"</span>, Process.SYSTEM_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.phone"</span>, RADIO_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.log"</span>, LOG_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.nfc"</span>, NFC_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.bluetooth"</span>, BLUETOOTH_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line">    mSettings.addSharedUserLPw(<span class="string">"android.uid.shell"</span>, SHELL_UID,</span><br><span class="line">            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">long</span> dexOptLRUThresholdInMinutes;</span><br><span class="line">    <span class="keyword">if</span> (mLazyDexOpt) &#123;</span><br><span class="line">        dexOptLRUThresholdInMinutes = <span class="number">30</span>; <span class="comment">// only last 30 minutes of apps for eng builds.</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        dexOptLRUThresholdInMinutes = <span class="number">7</span> * <span class="number">24</span> * <span class="number">60</span>; <span class="comment">// apps used in the 7 days for users.</span></span><br><span class="line">    &#125;</span><br><span class="line">    mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * <span class="number">60</span> * <span class="number">1000</span>;</span><br><span class="line">    ...</span><br><span class="line">    mInstaller = installer;</span><br><span class="line">    <span class="comment">// DexOpt工具类</span></span><br><span class="line">    mPackageDexOptimizer = <span class="keyword">new</span> PackageDexOptimizer(<span class="keyword">this</span>);</span><br><span class="line">    mMoveCallbacks = <span class="keyword">new</span> MoveCallbacks(FgThread.get().getLooper());</span><br><span class="line">    mOnPermissionChangeListeners = <span class="keyword">new</span> OnPermissionChangeListeners(</span><br><span class="line">                FgThread.get().getLooper());</span><br><span class="line">    getDefaultDisplayMetrics(context, mMetrics);</span><br><span class="line">    <span class="comment">// 读取系统默认的权限</span></span><br><span class="line">    SystemConfig systemConfig = SystemConfig.getInstance();</span><br><span class="line">    mGlobalGids = systemConfig.getGlobalGids();</span><br><span class="line">    mSystemPermissions = systemConfig.getSystemPermissions();</span><br><span class="line">    mAvailableFeatures = systemConfig.getAvailableFeatures();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这里上了两把锁: mInstallLock是安装APK时需要用到的锁；mPackage是更新APK信息时需要的锁</span></span><br><span class="line">    <span class="keyword">synchronized</span> (mInstallLock) &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (mPackages) &#123;</span><br><span class="line">        <span class="comment">// 构建一个后台线程，并将线程的消息队列绑定到Handler</span></span><br><span class="line">        mHandlerThread = <span class="keyword">new</span> ServiceThread(TAG,</span><br><span class="line">                Process.THREAD_PRIORITY_BACKGROUND, <span class="keyword">true</span> <span class="comment">/*allowIo*/</span>);</span><br><span class="line">        mHandlerThread.start();</span><br><span class="line">        mHandler = <span class="keyword">new</span> PackageHandler(mHandlerThread.getLooper());</span><br><span class="line">        <span class="comment">// 将PMS加入Watchdog的监控列表</span></span><br><span class="line">        Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 初始化一些文件目录</span></span><br><span class="line">        File dataDir = Environment.getDataDirectory();</span><br><span class="line">        mAppDataDir = <span class="keyword">new</span> File(dataDir, <span class="string">"data"</span>);</span><br><span class="line">        mAppInstallDir = <span class="keyword">new</span> File(dataDir, <span class="string">"app"</span>);</span><br><span class="line">        mAppLib32InstallDir = <span class="keyword">new</span> File(dataDir, <span class="string">"app-lib"</span>);</span><br><span class="line">        mAsecInternalPath = <span class="keyword">new</span> File(dataDir, <span class="string">"app-asec"</span>).getPath();</span><br><span class="line">        mUserAppDataDir = <span class="keyword">new</span> File(dataDir, <span class="string">"user"</span>);</span><br><span class="line">        mDrmAppPrivateInstallDir = <span class="keyword">new</span> File(dataDir, <span class="string">"app-private"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 初始化系统权限</span></span><br><span class="line">        ArrayMap&lt;String, SystemConfig.PermissionEntry&gt; permConfig</span><br><span class="line">                = systemConfig.getPermissions();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;permConfig.size(); i++) &#123;</span><br><span class="line">            SystemConfig.PermissionEntry perm = permConfig.valueAt(i);</span><br><span class="line">            BasePermission bp = mSettings.mPermissions.get(perm.name);</span><br><span class="line">            <span class="keyword">if</span> (bp == <span class="keyword">null</span>) &#123;</span><br><span class="line">                bp = <span class="keyword">new</span> BasePermission(perm.name, <span class="string">"android"</span>, BasePermission.TYPE_BUILTIN);</span><br><span class="line">                mSettings.mPermissions.put(perm.name, bp);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (perm.gids != <span class="keyword">null</span>) &#123;</span><br><span class="line">                bp.setGids(perm.gids, perm.perUser);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 初始化PMS中的ShareLibraries</span></span><br><span class="line">        ArrayMap&lt;String, String&gt; libConfig = systemConfig.getSharedLibraries();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;libConfig.size(); i++) &#123;</span><br><span class="line">            mSharedLibraries.put(libConfig.keyAt(i),</span><br><span class="line">                    <span class="keyword">new</span> SharedLibraryEntry(libConfig.valueAt(i), <span class="keyword">null</span>));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 读取mac_permission.xml文件的内容</span></span><br><span class="line">        mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();</span><br><span class="line"></span><br><span class="line">        mRestoredSettings = mSettings.readLPw(<span class="keyword">this</span>, sUserManager.getUsers(<span class="keyword">false</span>),</span><br><span class="line">                mSdkVersion, mOnlyCore);</span><br><span class="line">        String customResolverActivity = Resources.getSystem().getString(</span><br><span class="line">                        R.string.config_customResolverActivity);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 未完接代码片段2</span></span><br></pre></td></tr></table></figure><p>【代码片段1】完成了很多PMS的属性初始化操作，几个重要的属性如下：</p><ul><li><p><strong>mSettings：</strong>PMS内部有一个Settings数据结构，用于维护所有包的信息。写过Android应用程序的朋友可能知道两个APK可以运行在相同的进程中，前提是两个APK具有相同的签名和ShareUid。Android系统中定义了一些默认的ShareUid，譬如android.uid.system表示系统进程的UID，如果有一个APK想要运行在系统进程中，则其需要在AndroidManifest.xml文件中声明ShareUid为android.uid.system，并且该APK的签名必须与framework-res.apk的签名一致，即platform签名。</p><p>PMS在创建完Settings对象之后，便把很多系统默认的ShareUid加入其中。</p></li></ul><p>• <strong>mInstaller：</strong>Installer是一个系统服务，它封装了很多PMS进行包管理需要用到的函数，譬如install()、 dexopt()、rename()等。在Installer内部，其实是通过Socket连接installd，将执行指令发送到installd完成具体的操作。</p><p>• <strong>mPackageDexOptimizer:</strong> 进行Dex优化的工具类。对于一个APK而言，编译后其APK包中可执行文件的格式dex，安装到了手机上以后，需要经过文件格式转化才能运行，譬如APK需要转换成oat格式才能在ART虚拟机上运行，文件格式转换的过程就叫DexOpt。</p><p>  DalvikVM的时代，Android可执行文件的格式是dex，有一种进一步优化的格式叫odex；ART虚拟机的时代，Android可执行文件的格式是oat。虽然都叫做DexOpt，但在DalvikVM和ART两种不同虚拟机的时代分别有不同的内涵。</p><p>• <strong>SystemConfig:</strong> 系统全局的配置信息的数据结构。原始的数据来源于/system/etc/sysconfig和/system/etc/permissions目录下的XML文件,XML文件的详细信息见 Android中PackageManagerService相关的文件 这篇文章，在SystemConfig对象构建时，会读取这两个目录下所有XML文件的内容，主要有以下几个维度：</p><ul><li><p>权限与GID的映射关系，譬如，以下内容表示属于inet这个组的用户都拥有android.permission.INTERNET权限：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;permission name=<span class="string">"android.permission.INTERNET"</span> &gt;</span><br><span class="line">  &lt;group git=<span class="string">"inet"</span>&gt;</span><br><span class="line">&lt;/permission&gt;</span><br></pre></td></tr></table></figure></li><li><p>权限与UID的映射关系，譬如，以下内容表示UID为meida用户拥有android.permission.CAMERA这个权限:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;assign-permission name=<span class="string">"android.permission.CAMERA"</span> uid=<span class="string">"media"</span> /&gt;</span><br></pre></td></tr></table></figure></li><li><p>公共库的定义。Android中有很多公共库，除了BOOTCLASSPATH中定义的，框架层还支持额外的扩展，譬如，以下内容表示公共库的包名和其路径的关系：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;library name=<span class="string">"android.test.runner"</span></span><br><span class="line">       file=<span class="string">"/system/framework/android.test.runner.jar"</span> /&gt;</span><br></pre></td></tr></table></figure></li></ul><p>• <strong>mHandler：</strong> 创建PackageHandler对象，将其绑定到一个后台线程的消息队列。可想而知，一些厚重的活，譬如安装APK，就交由这个后台线程完成了。由于PMS是一个重要的系统服务，这个后台线程的消息队列如果过于忙碌，则会导致系统一直卡住，所以需要将这个消息队列加入Watchdog的监控列表，以便在这种情况下，Watchdog可以做出一些应急操作。</p><p>• <strong>初始化一些/data文件目录：</strong>应用程序的安装和运行都需要用到Data分区，PMS会在Data分区新建一些子目录。</p><p>• <strong>初始化系统权限：</strong>在SystemConfig初始化的时候，从/system/etc/permissions和/system/etc/sysconfig目录下读取了XML文件，这些信息要添加到Settings这个数据结构中。Android设计了一个BasePermission的数据结构，主要用于保存权限与包名之间的映射关系，此处，添加的权限是从SystemConfig中取出，包名是android，也就是先将系统权限添加到Settings中。</p><p>• <strong>mFoundPolicyFile:</strong> 有了SeLinux以后，Android会为每个文件打上SE Label，对于APK而言，打SE Label的准则就是签名，即根据签名信息打上不同的SE Label。Android将签名分类成为platform，testkey, media等，签名与类别的映射关系就存在一个叫mac_permission.xml的文件中。此处，需要读取该文件的内容。</p><h3 id="PMS-系统文件Dexopt扫描"><a href="#PMS-系统文件Dexopt扫描" class="headerlink" title="PMS 系统文件Dexopt扫描"></a>PMS 系统文件Dexopt扫描</h3><p>在完成部分属性的初始化之后，PMS要进入扫描安装阶段了。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//代码片段2：DexOpt处理，扫描系统文件</span></span><br><span class="line">    <span class="comment">// 关键日志，开始扫描系统APP</span></span><br><span class="line">    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,</span><br><span class="line">            startTime);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置扫描参数</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;</span><br><span class="line">    <span class="comment">// 1. 构建数组变量用于保存已经做过DexOpt的文件，后文中，会往这个数组变量中添加元素</span></span><br><span class="line">    <span class="keyword">final</span> ArraySet&lt;String&gt; alreadyDexOpted = <span class="keyword">new</span> ArraySet&lt;String&gt;();</span><br><span class="line">    <span class="keyword">final</span> String bootClassPath = System.getenv(<span class="string">"BOOTCLASSPATH"</span>);</span><br><span class="line">    <span class="keyword">final</span> String systemServerClassPath = System.getenv(<span class="string">"SYSTEMSERVERCLASSPATH"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// BOOTCLASSPATH环境变量所定义的文件已经做过DexOpt</span></span><br><span class="line">    <span class="keyword">if</span> (bootClassPath != <span class="keyword">null</span>) &#123;</span><br><span class="line">        String[] bootClassPathElements = splitString(bootClassPath, <span class="string">':'</span>);</span><br><span class="line">        <span class="keyword">for</span> (String element : bootClassPathElements) &#123;</span><br><span class="line">            alreadyDexOpted.add(element);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// SYSTEMSERVERCLASSPATH环境变量所定义的文件已经做过了DexOpt</span></span><br><span class="line">    <span class="keyword">if</span> (systemServerClassPath != <span class="keyword">null</span>) &#123;</span><br><span class="line">        String[] systemServerClassPathElements = splitString(systemServerClassPath, <span class="string">':'</span>);</span><br><span class="line">        <span class="keyword">for</span> (String element : systemServerClassPathElements) &#123;</span><br><span class="line">            alreadyDexOpted.add(element);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取指令集，以便后续进行DexOpt</span></span><br><span class="line">    <span class="keyword">final</span> List&lt;String&gt; allInstructionSets = InstructionSets.getAllInstructionSets();</span><br><span class="line">    <span class="keyword">final</span> String[] dexCodeInstructionSets =</span><br><span class="line">            getDexCodeInstructionSets(</span><br><span class="line">                        allInstructionSets.toArray(<span class="keyword">new</span> String[allInstructionSets.size()]));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 公共库是定义在 etc/sysconfig 和 etc/permissions 文件夹下的XML文件中</span></span><br><span class="line">    <span class="comment">// 需要这些公共库进行DexOpt处理</span></span><br><span class="line">    <span class="keyword">if</span> (mSharedLibraries.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (String dexCodeInstructionSet : dexCodeInstructionSets) &#123;</span><br><span class="line">            <span class="keyword">for</span> (SharedLibraryEntry libEntry : mSharedLibraries.values()) &#123;</span><br><span class="line">                <span class="keyword">final</span> String lib = libEntry.path;</span><br><span class="line">                <span class="keyword">if</span> (lib == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">int</span> dexoptNeeded = DexFile.getDexOptNeeded(lib, <span class="keyword">null</span>, dexCodeInstructionSet, <span class="keyword">false</span>);</span><br><span class="line">                    <span class="keyword">if</span> (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) &#123;</span><br><span class="line">                        alreadyDexOpted.add(lib);</span><br><span class="line">                        mInstaller.dexopt(lib, Process.SYSTEM_UID, <span class="keyword">true</span>, dexCodeInstructionSet, dexoptNeeded);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (...)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    File frameworkDir = <span class="keyword">new</span> File(Environment.getRootDirectory(), <span class="string">"framework"</span>);</span><br><span class="line">    alreadyDexOpted.add(frameworkDir.getPath() + <span class="string">"/framework-res.apk"</span>);</span><br><span class="line">    alreadyDexOpted.add(frameworkDir.getPath() + <span class="string">"/core-libart.jar"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// system/framework目录下，除了framework-res.apk和core-libart.jar这两个文件外</span></span><br><span class="line">    <span class="comment">// 其他的APK和JAR文件都需要进行DexOpt处理</span></span><br><span class="line">    String[] frameworkFiles = frameworkDir.list();</span><br><span class="line">    <span class="keyword">if</span> (frameworkFiles != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (String dexCodeInstructionSet : dexCodeInstructionSets) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;frameworkFiles.length; i++) &#123;</span><br><span class="line">                File libPath = <span class="keyword">new</span> File(frameworkDir, frameworkFiles[i]);</span><br><span class="line">                String path = libPath.getPath();</span><br><span class="line">                <span class="keyword">if</span> (alreadyDexOpted.contains(path)) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (!path.endsWith(<span class="string">".apk"</span>) &amp;&amp; !path.endsWith(<span class="string">".jar"</span>)) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">int</span> dexoptNeeded = DexFile.getDexOptNeeded(path, <span class="keyword">null</span>, dexCodeInstructionSet, <span class="keyword">false</span>);</span><br><span class="line">                    <span class="keyword">if</span> (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) &#123;</span><br><span class="line">                        mInstaller.dexopt(path, Process.SYSTEM_UID, <span class="keyword">true</span>, dexCodeInstructionSet, dexoptNeeded);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span>(...)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. Android M的APK授权机制有了变化，此处是与授权相关的版本兼容处理</span></span><br><span class="line">    <span class="keyword">final</span> VersionInfo ver = mSettings.getInternalVersion();</span><br><span class="line">    mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);</span><br><span class="line">    mPromoteSystemApps =</span><br><span class="line">            mIsUpgrade &amp;&amp; ver.sdkVersion &lt;= Build.VERSION_CODES.LOLLIPOP_MR1;</span><br><span class="line">    <span class="keyword">if</span> (mPromoteSystemApps) &#123;</span><br><span class="line">        Iterator&lt;PackageSetting&gt; pkgSettingIter = mSettings.mPackages.values().iterator();</span><br><span class="line">        <span class="keyword">while</span> (pkgSettingIter.hasNext()) &#123;</span><br><span class="line">            PackageSetting ps = pkgSettingIter.next();</span><br><span class="line">            <span class="keyword">if</span> (isSystemApp(ps)) &#123;</span><br><span class="line">                mExistingSystemPackages.add(ps.name);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 扫描系统文件</span></span><br><span class="line">    File vendorOverlayDir = <span class="keyword">new</span> File(VENDOR_OVERLAY_DIR);</span><br><span class="line">    <span class="comment">// 扫描 /vendor/overlay 目录下的文件</span></span><br><span class="line">    scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 扫描 /system/framework 目录下的文件</span></span><br><span class="line">    scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR</span><br><span class="line">            | PackageParser.PARSE_IS_PRIVILEGED,</span><br><span class="line">            scanFlags | SCAN_NO_DEX, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 扫描 /system/priv-app 目录下的文件</span></span><br><span class="line">    <span class="keyword">final</span> File privilegedAppDir = <span class="keyword">new</span> File(Environment.getRootDirectory(), <span class="string">"priv-app"</span>);</span><br><span class="line">    scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR</span><br><span class="line">            | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 扫描 /system/app 目录下的文件</span></span><br><span class="line">    <span class="keyword">final</span> File systemAppDir = <span class="keyword">new</span> File(Environment.getRootDirectory(), <span class="string">"app"</span>);</span><br><span class="line">    scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 扫描 /vendor/app 目录下的文件</span></span><br><span class="line">    File vendorAppDir = <span class="keyword">new</span> File(<span class="string">"/vendor/app"</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        vendorAppDir = vendorAppDir.getCanonicalFile();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;&#125;</span><br><span class="line">    scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 扫描 /oem/app 目录下的文件</span></span><br><span class="line">    <span class="keyword">final</span> File oemAppDir = <span class="keyword">new</span> File(Environment.getOemDirectory(), <span class="string">"app"</span>);</span><br><span class="line">    scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, <span class="number">0</span>);</span><br><span class="line">    mInstaller.moveFiles();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4. 对扫描到的系统文件善后处理</span></span><br><span class="line">    <span class="keyword">final</span> List&lt;String&gt; possiblyDeletedUpdatedSystemApps = <span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">    <span class="keyword">if</span> (!mOnlyCore) &#123;</span><br><span class="line">        Iterator&lt;PackageSetting&gt; psit = mSettings.mPackages.values().iterator();</span><br><span class="line">        <span class="keyword">while</span> (psit.hasNext()) &#123;</span><br><span class="line">            PackageSetting ps = psit.next();</span><br><span class="line">            <span class="keyword">if</span> ((ps.pkgFlags &amp; ApplicationInfo.FLAG_SYSTEM) == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">final</span> PackageParser.Package scannedPkg = mPackages.get(ps.name);</span><br><span class="line">            <span class="keyword">if</span> (scannedPkg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (mSettings.isDisabledSystemPackageLPr(ps.name)) &#123;</span><br><span class="line">                    removePackageLI(ps, <span class="keyword">true</span>);</span><br><span class="line">                    mExpectingBetter.put(ps.name, ps.codePath);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!mSettings.isDisabledSystemPackageLPr(ps.name)) &#123;</span><br><span class="line">                psit.remove();</span><br><span class="line">                removeDataDirsLI(<span class="keyword">null</span>, ps.name);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">final</span> PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);</span><br><span class="line">                <span class="keyword">if</span> (disabledPs.codePath == <span class="keyword">null</span> || !disabledPs.codePath.exists()) &#123;</span><br><span class="line">                    possiblyDeletedUpdatedSystemApps.add(ps.name);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ArrayList&lt;PackageSetting&gt; deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; deletePkgsList.size(); i++) &#123;</span><br><span class="line">        cleanupInstallFailedPackage(deletePkgsList.get(i));</span><br><span class="line">    &#125;</span><br><span class="line">    deleteTempPackageFiles();</span><br><span class="line">    mSettings.pruneSharedUsersLPw();</span><br><span class="line"><span class="comment">// 未完接代码片段3</span></span><br></pre></td></tr></table></figure></p><p>【代码片段2】的主体逻辑如下：</p><ol><li><p>通过不断往alreadyDexOpted数组中填充元素，来略过不需要做DexOpt的文件：BOOTCLASSPATH和SYSTEMSERVERPATH这两个环境变量中定义的文件、system/framework-res.apk、system/core-libart.jar。除略过的文件外，其他APK和JAR文件都是需要做DexOpt处理的，通过调用Installer.dexopt()函数完成，这个函数只是将dexopt命令发送给installd。</p></li><li><p>由于Android M的APK授权机制发生了变化，在扫描系统文件之前，做了一些简单的记录，以便后续的授权处理：</p><blockquote><p>mIsUpgrade：如果当前版本的指纹与历史版本的指纹信息不一致，表示当前版本是一次OTA升级上来更新版本<br>mPromoteSystemApps：如果历史版本是Android M之前的版本(ver.sdkVersion &lt;= Build.VERSION_CODES.LOLLIPOP_MR1)，当前又有版本升级，则需要用一个布尔变量，表示当前需要对系统应用的授权做特殊处理，此时会先把已有的系统应用都保存在mExistingSystemPackages这个数组中</p></blockquote></li><li><p>扫描系统文件，PMS中所有的文件扫描都是调用scanDirLI()函数，扫描系统文件重要的参数就是 PackageParser.PARSE_IS_SYSTEM和PackageParser.PARSE_IS_SYSTEM_DIR，在后文中我们会剖析这个函数。此处，需要注意的是被扫描目录的顺序，这个顺序意味着：先被扫描到的文件，就是最终被用到的文件。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/vendor/overlay &gt;&gt; /system/framework &gt;&gt; /system/priv-app &gt;&gt; /system/app &gt;&gt; /vendor/app &gt;&gt; /oem/app</span><br></pre></td></tr></table></figure></li><li><p>possiblyDeletedUpdatedSystemApps这个变量表示“可能被删除的系统APP”，这是一个什么概念呢？除了 possiblyDeletedUpdatedSystemApps ，还有mExpectingBetter，表示当前这个APK有更好的选择，这又是什么概念呢？对于一个系统APP而言，在一次OTA升级的过程中，有三种可能：</p><ul><li>保持原状。即这个系统APP没有任何更新。</li><li>更新版本。即新的OTA版本中，这个系统APP有更新。<br>-不复存在。在新的OTA版本中已经删除了这个系统APP。<br>当系统APP升级过后，PMS的Settings中会将原来的系统APP标识为Disable状态，这时候通过Settings.isDisabledSystemPackageLPr()函数调用便返回了false。因此，如果系统APP有更新版本，则属于mExpectingBetter这一类，接下来会扫描Data分区的文件，更新的系统APP就安装在Data分区。</li></ul></li></ol><p>如果一个系统APP不复存在，而且也没有被标记为Disable状态，说明这个系统APP已经彻底不存在了，需要把其在Data分区下的数据删除；如果不复存在的系统APP被标记为Disable状态，那还不能确定该系统APP是否已经被删除，因为还没有扫描Data分区的文件，所以，只能暂时将其放到possiblyDeletedUpdatedSystemApps变量中，表示“可能被删除”，在扫描Data分区之前，这是不能确定的。</p><h3 id="PMS-Data分区扫描"><a href="#PMS-Data分区扫描" class="headerlink" title="PMS Data分区扫描"></a>PMS Data分区扫描</h3><p>扫描完系统文件之后，接下来会扫描Data分区的文件。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码片段3：扫描Data分区文件，更新公共库信息</span></span><br><span class="line">    <span class="keyword">if</span> (!mOnlyCore) &#123;</span><br><span class="line">        <span class="comment">// 关键日志，PMS对Data分区的文件扫描开始了</span></span><br><span class="line">        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,</span><br><span class="line">                SystemClock.uptimeMillis());</span><br><span class="line">        <span class="comment">// 1. 扫描Data分区的文件目录</span></span><br><span class="line">        scanDirLI(mAppInstallDir, <span class="number">0</span>, scanFlags | SCAN_REQUIRE_KNOWN, <span class="number">0</span>);</span><br><span class="line">        scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,</span><br><span class="line">                scanFlags | SCAN_REQUIRE_KNOWN, <span class="number">0</span>);</span><br><span class="line">        <span class="comment">// 2. 扫描完Data分区后，处理“可能被删除的系统应用”</span></span><br><span class="line">        <span class="keyword">for</span> (String deletedAppName : possiblyDeletedUpdatedSystemApps) &#123;</span><br><span class="line">            PackageParser.Package deletedPkg = mPackages.get(deletedAppName);</span><br><span class="line">            mSettings.removeDisabledSystemPackageLPw(deletedAppName);</span><br><span class="line">            String msg;</span><br><span class="line">            <span class="keyword">if</span> (deletedPkg == <span class="keyword">null</span>) &#123;</span><br><span class="line">                removeDataDirsLI(<span class="keyword">null</span>, deletedAppName);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               deletedPkg.applicationInfo.flags &amp;= ~ApplicationInfo.FLAG_SYSTEM;</span><br><span class="line">               PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);</span><br><span class="line">               deletedPs.pkgFlags &amp;= ~ApplicationInfo.FLAG_SYSTEM;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 3. 处理有版本更新的系统应用</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; mExpectingBetter.size(); i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> String packageName = mExpectingBetter.keyAt(i);</span><br><span class="line">            <span class="keyword">if</span> (!mPackages.containsKey(packageName)) &#123;</span><br><span class="line">                <span class="keyword">final</span> File scanFile = mExpectingBetter.valueAt(i);</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> reparseFlags;</span><br><span class="line">                <span class="comment">// 设置重新扫描的解析参数</span></span><br><span class="line">                <span class="keyword">if</span> (FileUtils.contains(privilegedAppDir, scanFile)) &#123;</span><br><span class="line">                    reparseFlags = PackageParser.PARSE_IS_SYSTEM</span><br><span class="line">                            | PackageParser.PARSE_IS_SYSTEM_DIR</span><br><span class="line">                            | PackageParser.PARSE_IS_PRIVILEGED;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;...&#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 将原来的系统应用重新置为Enable状态</span></span><br><span class="line">                mSettings.enableSystemPackageLPw(packageName);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    scanPackageLI(scanFile, reparseFlags, scanFlags, <span class="number">0</span>, <span class="keyword">null</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (PackageManagerException e) &#123; ... &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="comment">// end of "if (!mOnlyCore)"</span></span><br><span class="line">    mExpectingBetter.clear();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4. 处理公共库</span></span><br><span class="line">    updateAllSharedLibrariesLPw();</span><br><span class="line">    <span class="keyword">for</span> (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) &#123;</span><br><span class="line">         adjustCpuAbisForSharedUserLPw(setting.packages, <span class="keyword">null</span> <span class="comment">/* scanned package */</span>,</span><br><span class="line">                 <span class="keyword">false</span> <span class="comment">/* force dexopt */</span>, <span class="keyword">false</span> <span class="comment">/* defer dexopt */</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mPackageUsage.readLP();</span><br><span class="line">    <span class="comment">// 关键日志，PMS扫描结束了</span></span><br><span class="line">    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,</span><br><span class="line">            SystemClock.uptimeMillis());</span><br><span class="line"><span class="comment">// 未完接代码片段4</span></span><br></pre></td></tr></table></figure></p><p>Data分区文件的扫描都被mOnlyCore这个布尔变量笼罩，当其为true时，表示只需要扫描系统文件；当其为false时，才会扫描Data分区文件。【代码片段3】的主体逻辑如下：</p><ol><li><p>调用PMS.scanDirLI()函数扫描 /data/app 和 /data/app-private两个目录的文件。后文会详细剖析该函数。</p></li><li><p>扫描完Data分区的文件后，需要对之前系统文件的余孽做一些处理，第一类是possiblyDeletedUpdatedSystemApps，因为在扫描Data分区文件之前，不能确定系统应用有没有被彻底删除，如果在Data分区也无法找到了不复存在的系统应用，则需要彻底删除该系统应用；如果在Data分区找到了不复存在的系统应用，则需要去除其系统应用的标识。</p></li><li><p>另外一类系统应用的余孽是mExpectingBetter，表示系统应用已经升级过。如果在Data分区无法找到这些升级过的系统应用，那很可能是用户在OTA升级时，清除了Data分区的数据，对于这种场景，需要重新扫描一下该应用原来位于系统分区的文件。</p></li><li><p>SystemConfig中定义了公共库，在APK的AndroidManifest.xml文件中，会通过<use-library>标签标记该APK动态依赖的公共库，此处的逻辑就是将APK与SystemConfig中的公共库关联起来。如果APK使用的公共库并不存在，则会抛出异常(INSTALL_FAILED_MISSING_SHARED_LIBRARY)。</use-library></p></li></ol><h3 id="收尾工作"><a href="#收尾工作" class="headerlink" title="收尾工作"></a>收尾工作</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码片段4：收尾工作</span></span><br><span class="line">    <span class="comment">// 授权</span></span><br><span class="line">    <span class="keyword">int</span> updateFlags = UPDATE_PERMISSIONS_ALL;</span><br><span class="line">    <span class="keyword">if</span> (ver.sdkVersion != mSdkVersion) &#123;</span><br><span class="line">        updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;</span><br><span class="line">    &#125;</span><br><span class="line">    updatePermissionsLPw(<span class="keyword">null</span>, <span class="keyword">null</span>, updateFlags);</span><br><span class="line">    ver.sdkVersion = mSdkVersion;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 多用户场景下的版本兼容处理</span></span><br><span class="line">    <span class="keyword">if</span> (!onlyCore &amp;&amp; (mPromoteSystemApps || !mRestoredSettings)) &#123;</span><br><span class="line">        <span class="keyword">for</span> (UserInfo user : sUserManager.getUsers(<span class="keyword">true</span>)) &#123;</span><br><span class="line">            mSettings.applyDefaultPreferredAppsLPw(<span class="keyword">this</span>, user.id);</span><br><span class="line">            applyFactoryDefaultBrowserLPw(user.id);</span><br><span class="line">            primeDomainVerificationsLPw(user.id);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果是升级新版本，则需要清除已有的Code cache目录</span></span><br><span class="line">    <span class="keyword">if</span> (mIsUpgrade &amp;&amp; !onlyCore) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; mSettings.mPackages.size(); i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> PackageSetting ps = mSettings.mPackages.valueAt(i);</span><br><span class="line">            <span class="keyword">if</span> (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) &#123;</span><br><span class="line">                deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ver.fingerprint = Build.FINGERPRINT;</span><br><span class="line">    &#125;</span><br><span class="line">    checkDefaultBrowser();</span><br><span class="line">    mExistingSystemPackages.clear();</span><br><span class="line">    mPromoteSystemApps = <span class="keyword">false</span>;</span><br><span class="line">    ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;</span><br><span class="line">    mSettings.writeLPr();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关键日志，PMS已经启动完毕了</span></span><br><span class="line">    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,</span><br><span class="line">            SystemClock.uptimeMillis());</span><br><span class="line"></span><br><span class="line">    mRequiredVerifierPackage = getRequiredVerifierLPr();</span><br><span class="line">    mRequiredInstallerPackage = getRequiredInstallerLPr();</span><br><span class="line">    <span class="comment">// 初始化包安装服务</span></span><br><span class="line">    mInstallerService = <span class="keyword">new</span> PackageInstallerService(context, <span class="keyword">this</span>);</span><br><span class="line">    mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();</span><br><span class="line">    mIntentFilterVerifier = <span class="keyword">new</span> IntentVerifierProxy(mContext,</span><br><span class="line">            mIntentFilterVerifierComponent);</span><br><span class="line">    &#125; <span class="comment">// synchronized (mPackages)</span></span><br><span class="line">    &#125; <span class="comment">// synchronized (mInstallLock)</span></span><br><span class="line"></span><br><span class="line">    Runtime.getRuntime().gc();</span><br><span class="line">    LocalServices.addService(PackageManagerInternal.class, <span class="keyword">new</span> PackageManagerInternalImpl());</span><br><span class="line">&#125; <span class="comment">// PMS构造函数完结</span></span><br></pre></td></tr></table></figure><p>【代码片段4】主要进行一些收尾工作，有几个关键点：</p><ul><li>授权，通过调用PMS.updatePermissionsLPw()函数，后文会详细分析</li><li>版本兼容处理，Android M引入了多用户，需要更新每个APK关联到的userid</li><li>将PMS的Settings信息写入/system/packages.xml文件中，Settings是PMS中所有包信息的汇总的数据结构，PMS对包的管理极其依赖于这个数据结构。</li><li>初始化包安装服务PackageInstallerService。<br>至此，PMS对象的构建过程已经分析完毕，整个逻辑还是较为清晰的，但其实这是一个非常耗时的过程，开机时间大部分都耗在文件扫描上。</li></ul><h2 id="文件扫描"><a href="#文件扫描" class="headerlink" title="文件扫描"></a>文件扫描</h2><p>scanDirLI()只是文件扫描的起点，由此引发出一串的与文件扫描相关的函数。<br><img src="/Android-PackageManagerService-Analysis/5-packagemanager-scan-seq.png" alt="文件扫描"><br>接下来，笔者会按照调用时序，对关键函数进行分析。PMS对象构建时，待扫描的目录有很多，不同目录的文件扫描，只是在扫描参数上略有区别，整体逻辑上并无不同。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">scanDirLI</span><span class="params">(File dir, <span class="keyword">int</span> parseFlags, <span class="keyword">int</span> scanFlags, <span class="keyword">long</span> currentTime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> File[] files = dir.listFiles();</span><br><span class="line">    <span class="keyword">if</span> (ArrayUtils.isEmpty(files)) &#123;</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (File file : files) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> isPackage = (isApkFile(file) || file.isDirectory())</span><br><span class="line">            &amp;&amp; !PackageInstallerService.isStageName(file.getName());</span><br><span class="line">        <span class="keyword">if</span> (!isPackage) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,</span><br><span class="line">                    scanFlags, currentTime, <span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (PackageManagerException e) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((parseFlags &amp; PackageParser.PARSE_IS_SYSTEM) == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                    e.error == PackageManager.INSTALL_FAILED_INVALID_APK) &#123;</span><br><span class="line">                <span class="comment">// 如果扫描Data分区的APK失败，则删除Data分区扫描失败的文件</span></span><br><span class="line">                <span class="keyword">if</span> (file.isDirectory()) &#123;</span><br><span class="line">                    mInstaller.rmPackageDir(file.getAbsolutePath());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    file.delete();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>Ongoing—</p><p>## </p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://duanqz.github.io/2017-01-04-Package-Manage-Mechanism#1-%E6%A6%82%E8%A6%81" target="_blank" rel="noopener">Android包管理机制</a><br><a href="https://blog.csdn.net/linliang815/article/details/76640262" target="_blank" rel="noopener">Android 包管理（PackageManagerService）</a><br><a href="https://blog.csdn.net/innost/article/details/47253179" target="_blank" rel="noopener">第4章  深入理解PackageManagerService</a><br><a href="http://wiki.jikexueyuan.com/project/deep-android-v2/packagemanagerservice.html" target="_blank" rel="noopener">深入理解 PackageManagerService</a><br><a href="https://blog.csdn.net/gaugamela/article/details/52619720" target="_blank" rel="noopener">Android7.0 PackageManagerService (1) 通信结构、启动和main函数</a><br><a href="https://www.cnblogs.com/chenlong-50954265/p/5729553.html" target="_blank" rel="noopener">Android PackageManagerService详细分析</a><br><a href="https://blog.csdn.net/xiashaohua/article/details/75129690" target="_blank" rel="noopener">Android8.0 PackageManagerService相关 – APK安装和install 的变更和源码浅析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概要&quot;&gt;&lt;a href=&quot;#概要&quot; class=&quot;headerlink&quot; title=&quot;概要&quot;&gt;&lt;/a&gt;概要&lt;/h1&gt;&lt;p&gt;每一个社会群落都有管理机制，其中有三个要素：被管理者、管理者以及管理机制的运转。在Android的世界中，有一处群落叫“包管理”，要研究An
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="PackageManagerService" scheme="http://yoursite.com/tags/PackageManagerService/"/>
    
  </entry>
  
  <entry>
    <title>Android中PackageManagerService相关的文件</title>
    <link href="http://yoursite.com/Android%E4%B8%ADPackageManagerService%E7%9B%B8%E5%85%B3%E7%9A%84%E6%96%87%E4%BB%B6/"/>
    <id>http://yoursite.com/Android中PackageManagerService相关的文件/</id>
    <published>2018-05-31T13:55:56.000Z</published>
    <updated>2018-08-07T07:50:07.306Z</updated>
    
    <content type="html"><![CDATA[<h1 id="system-etc-sysconfig-目录下的文件"><a href="#system-etc-sysconfig-目录下的文件" class="headerlink" title="/system/etc/sysconfig 目录下的文件"></a>/system/etc/sysconfig 目录下的文件</h1><h2 id="framework-sysconfig-xml"><a href="#framework-sysconfig-xml" class="headerlink" title="framework-sysconfig.xml"></a>framework-sysconfig.xml</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span>?&gt;</span><br><span class="line">&lt;!-- Copyright (C) 2017 The Android Open Source Project</span><br><span class="line"></span><br><span class="line">     Licensed under the Apache License, Version 2.0 (the <span class="string">"License"</span>);</span><br><span class="line">     you may not use this file except <span class="keyword">in</span> compliance with the License.</span><br><span class="line">     You may obtain a copy of the License at</span><br><span class="line"></span><br><span class="line">          http://www.apache.org/licenses/LICENSE-2.0</span><br><span class="line"></span><br><span class="line">     Unless required by applicable law or agreed to <span class="keyword">in</span> writing, software</span><br><span class="line">     distributed under the License is distributed on an <span class="string">"AS IS"</span> BASIS,</span><br><span class="line">     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><br><span class="line">     See the License <span class="keyword">for</span> the specific language governing permissions and</span><br><span class="line">     limitations under the License.</span><br><span class="line">--&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- These are configurations that must exist on all Android devices. --&gt;</span><br><span class="line">&lt;config&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- Broadcast actions that are currently exempted from O+ background</span><br><span class="line">         delivery restrictions. --&gt;</span><br><span class="line">    &lt;allow-implicit-broadcast action=<span class="string">"android.intent.action.SIM_STATE_CHANGED"</span> /&gt;</span><br><span class="line">    &lt;allow-implicit-broadcast action=<span class="string">"android.intent.action.PACKAGE_CHANGED"</span> /&gt;</span><br><span class="line">    &lt;allow-implicit-broadcast action=<span class="string">"android.intent.action.MEDIA_SCANNER_SCAN_FILE"</span> /&gt;</span><br><span class="line">    &lt;allow-implicit-broadcast action=<span class="string">"android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"</span> /&gt;</span><br><span class="line">    &lt;allow-implicit-broadcast action=<span class="string">"android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"</span> /&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- Whitelist of what components are permitted as backup data transports.  The</span><br><span class="line">         <span class="string">'service'</span> attribute here is a flattened ComponentName string. --&gt;</span><br><span class="line">    &lt;backup-transport-whitelisted-service</span><br><span class="line">        service=<span class="string">"android/com.android.internal.backup.LocalTransportService"</span> /&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- Whitelist of bundled applications <span class="built_in">which</span> all handle URLs to their websites by default --&gt;</span><br><span class="line">    &lt;app-link package=<span class="string">"com.android.carrierdefaultapp"</span> /&gt;</span><br><span class="line">&lt;/config&gt;</span><br></pre></td></tr></table></figure><h2 id="QualCommon-platform-下的qti-whitelist-xml"><a href="#QualCommon-platform-下的qti-whitelist-xml" class="headerlink" title="QualCommon platform 下的qti_whitelist.xml"></a>QualCommon platform 下的qti_whitelist.xml</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span> standalone=<span class="string">"yes"</span> ?&gt;</span><br><span class="line"></span><br><span class="line">&lt;!--</span><br><span class="line">/* Copyright (c) 2017, The Linux Foundation. All rights reserved.</span><br><span class="line"> *</span><br><span class="line"> * Redistribution and use <span class="keyword">in</span> <span class="built_in">source</span> and binary forms, with or without</span><br><span class="line"> * modification, are permitted provided that the following conditions are</span><br><span class="line"> * met:</span><br><span class="line"> *     * Redistributions of <span class="built_in">source</span> code must retain the above copyright</span><br><span class="line"> *       notice, this list of conditions and the following disclaimer.</span><br><span class="line"> *     * Redistributions <span class="keyword">in</span> binary form must reproduce the above</span><br><span class="line"> *       copyright notice, this list of conditions and the following</span><br><span class="line"> *       disclaimer <span class="keyword">in</span> the documentation and/or other materials provided</span><br><span class="line"> *       with the distribution.</span><br><span class="line"> *     * Neither the name of The Linux Foundation nor the names of its</span><br><span class="line"> *       contributors may be used to endorse or promote products derived</span><br><span class="line"> *       from this software without specific prior written permission.</span><br><span class="line"> *</span><br><span class="line"> * THIS SOFTWARE IS PROVIDED <span class="string">"AS IS"</span> AND ANY EXPRESS OR IMPLIED</span><br><span class="line"> * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF</span><br><span class="line"> * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT</span><br><span class="line"> * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS</span><br><span class="line"> * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR</span><br><span class="line"> * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF</span><br><span class="line"> * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR</span><br><span class="line"> * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,</span><br><span class="line"> * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE</span><br><span class="line"> * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN</span><br><span class="line"> * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span><br><span class="line"> */</span><br><span class="line">--&gt;</span><br><span class="line"></span><br><span class="line">&lt;config&gt;</span><br><span class="line">    &lt;!-- These are telephony components that need to freely run <span class="keyword">in</span> the background --&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.atfwd"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.telephonyservice"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qulacomm.qcrilmsgtunnel"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.ims"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.radioconfiginterface"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.simcontacts"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.autoregistration"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.server.wigigapp"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.StatsPollManager"</span> /&gt;</span><br><span class="line">    &lt;allow-in-power-save package=<span class="string">"com.qualcomm.qti.gsma.services.nfc"</span> /&gt;</span><br><span class="line">&lt;/config&gt;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;system-etc-sysconfig-目录下的文件&quot;&gt;&lt;a href=&quot;#system-etc-sysconfig-目录下的文件&quot; class=&quot;headerlink&quot; title=&quot;/system/etc/sysconfig 目录下的文件&quot;&gt;&lt;/a&gt;/sys
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="PackageManagerService" scheme="http://yoursite.com/tags/PackageManagerService/"/>
    
      <category term="File" scheme="http://yoursite.com/tags/File/"/>
    
  </entry>
  
  <entry>
    <title>Android系统性能调优工具</title>
    <link href="http://yoursite.com/Android-Performance/"/>
    <id>http://yoursite.com/Android-Performance/</id>
    <published>2018-05-28T13:23:33.000Z</published>
    <updated>2018-07-30T01:40:12.761Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>在软件开发过程中，想必很多读者都遇到过系统性能问题。而解决系统性能问题的几个主要步骤是：</p><ul><li>测评：对系统进行大量有针对性的测试，以得到合适的测试数据。</li><li>分析系统瓶颈：分析测试数据，找到其中的hotspot（热点，即bottleneck）。</li><li>性能优化：对hotspot相关的代码进行优化。<br>由上述步骤可知，性能优化的目标对象是hotspot。如果找到的hotspot并非真正的热点，则性能优化的结果必然是事倍功半甚至竹篮打水一场空。所以，作为Android性能调优相关知识的第一部分，本篇首先将向读者介绍Android平台中三个重要的性能测试工具，它们能很好得帮助开发者找到hotspot。</li></ul><p>Android官方的性能和功耗的优化介绍： <a href="https://developer.android.com/topic/performance/" target="_blank" rel="noopener">https://developer.android.com/topic/performance/</a></p><h1 id="Android性能相关的关键词"><a href="#Android性能相关的关键词" class="headerlink" title="Android性能相关的关键词:"></a>Android性能相关的关键词:</h1><ol><li><a href="https://developer.android.com/topic/performance/vitals/" target="_blank" rel="noopener">Android Vitals</a>：Android性能指标</li><li>[TraceView] ：Android性能分析工具</li><li>[dmtracedump] :Google提供的工具，能画函数调用图，dmtracedump和TraceView是一对兄弟，一起使用，用来分析性能</li></ol><h2 id="Android-性能指标"><a href="#Android-性能指标" class="headerlink" title="Android 性能指标"></a>Android 性能指标</h2><p>Android的性能指标，可以参考Google提供的 Android Vitals 仪表盘只能在Google play store上使用，用来监测APP性能数据.<br>见<a href="https://support.google.com/googleplay/android-developer/answer/7385505" target="_blank" rel="noopener">https://support.google.com/googleplay/android-developer/answer/7385505</a></p><ol><li>ANR比例</li><li>Crash 比例</li><li>过度唤醒 WakeLock ,AlarmManager</li><li>过度的后台Wifi扫描</li><li>过度的后台网络使用</li><li>渲染慢,UI 渲染要在16ms以下，60fps即60帧/s以上，不然的话就是Jank(不合格)，用户明显能够感受到卡顿现象。<blockquote><p>识别Jank的方法：<br>a) 通过在 开发者选项中打开 GPU 渲染开关后，查看柱形条<br>b)使用systrace<br>c)</p></blockquote></li><li></li></ol><h2 id="Android-性能指标监测工具"><a href="#Android-性能指标监测工具" class="headerlink" title="Android 性能指标监测工具"></a>Android 性能指标监测工具</h2><p><a href="https://blog.csdn.net/u011974987/article/details/51753774" target="_blank" rel="noopener">Android 性能指标检测工具</a></p><p>Android 常见问题:</p><ul><li>内存泄漏：不使用的内存空间(对象)一直被引用着没有得到释放。</li><li>内存抖动：短时间内大量创建对象又在短时间内频繁触发GC导致内存波动很大，android虚拟机执行GC操作时需要耗费CPU性能频繁GC会到来严重的性能问题。内存抖动是因为大量的对象被创建又在短时间内马上被释放。瞬间产生大量的对象会严重占用Young Generation的内存区域，当达到阀值，剩余空间不够的时候，也会触发GC。即使每次分配的对象占用了很少的内存，但是他们叠加在一起会增加Heap的压力，从而触发更多其他类型的GC。这个操作有可能会影响到帧率，并使得用户感知到性能问题。</li><li>内存溢出：内存泄漏到一定程度超出系统给进程分配的内存大小就会造成内存溢出程序奔溃，或者加载一个或多个大型文件(图片)到内存中也会导致内存溢出。</li><li>ANR：应用无响应，代码效率过低或者在主线程执行耗时操作。</li></ul><p>内存监控工具如下:</p><p><a href="https://blog.csdn.net/ekeuy/article/details/48661595" target="_blank" rel="noopener">Android性能测试工具列表</a></p><h1 id="TraceView"><a href="#TraceView" class="headerlink" title="TraceView"></a>TraceView</h1><h2 id="TraceView介绍与用途"><a href="#TraceView介绍与用途" class="headerlink" title="TraceView介绍与用途"></a>TraceView介绍与用途</h2><p>TraceView 是 Android SDK 中内置的一个工具，它可以加载 trace 文件，<strong>用图形的形式展示代码的执行时间、次数及调用栈</strong>，便于我们分析。</p><p>从代码层面分析性能问题，针对每个方法来分析，比如当我们发现我们的应用出现卡顿的时候，我们可以来分析出现卡顿时在方法的调用上有没有很耗时的操作，关注以下两个问题：</p><pre><code>- 调用次数不多，但是每一次执行都很耗时- 方法耗时不大，但是调用次数太多</code></pre><p>简单一点来说就是我们能找到频繁被调用的方法，也能找到执行非常耗时的方法，前者可能会造成Cpu频繁调用，手机发烫的问题，后者就是卡顿的问题</p><p>两方面用途： </p><ol><li>查看跟踪代码的执行时间，分析哪些是耗时操作，主要用于分析方法和线程</li><li>可以用于跟踪方法的调用，尤其是android Framework层的方法调用关系</li></ol><p>Traceview是Android平台特有的数据采集和分析工具，它主要用于分析Android中应用程序的hotspot。Traceview本身只是一个数据分析工具，而数据的采集则需要使用Android SDK中的Debug类或者利用DDMS工具。二者的用法如下：</p><ul><li>开发者在一些关键代码段开始前调用Android SDK中Debug类的startMethodTracing函数，并在关键代码段结束前调用stopMethodTracing函数。这两个函数运行过程中将采集运行时间内该应用所有线程（注意，只能是Java线程）的函数执行情况，并将采集数据保存到/mnt/sdcard/下的一个文件中。开发者然后需要利用SDK中的Traceview工具来分析这些数据。</li><li>借助Android SDK中的DDMS工具。DDMS可采集系统中某个正在运行的进程的函数调用信息。对开发者而言，此方法适用于没有目标应用源代码的情况。DDMS工具中Traceview的使用如图1-1所示。<br><img src="https://img-blog.csdn.net/20130602213318781" alt="图1-1"><br><center>图1-1  DDMS中Traceview使用示意图</center><br>点击图1-1中所示按钮即可以采集目标进程的数据。当停止采集时，DDMS会自动触发Traceview工具来浏览采集数据。</li></ul><h2 id="TraceView的使用"><a href="#TraceView的使用" class="headerlink" title="TraceView的使用"></a>TraceView的使用</h2><p><a href="https://blog.csdn.net/superxlcr/article/details/78219673" target="_blank" rel="noopener">TraceView工具如何使用</a><br>Google官方TraceView 教程：</p><ol><li>使用TraceView收集log <a href="https://developer.android.com/studio/profile/generate-trace-logs" target="_blank" rel="noopener">https://developer.android.com/studio/profile/generate-trace-logs</a></li><li>分析TraceView log <a href="https://developer.android.com/studio/profile/traceview#timelinepanel" target="_blank" rel="noopener">https://developer.android.com/studio/profile/traceview#timelinepanel</a></li></ol><p>Android代码调试工具 traceview 和 dmtracedump的波折演绎 <a href="https://blog.csdn.net/yiyaaixuexi/article/details/6716884" target="_blank" rel="noopener">https://blog.csdn.net/yiyaaixuexi/article/details/6716884</a> 此文的优点：可以使用dmtracedump来输出函数调用关系图</p><p>以下章节来自：<br><a href="https://blog.csdn.net/u011240877/article/details/54347396" target="_blank" rel="noopener">Android 性能优化：使用 TraceView 找到卡顿的元凶</a></p><h3 id="生成TraceView文件"><a href="#生成TraceView文件" class="headerlink" title="生成TraceView文件"></a>生成TraceView文件</h3><p>生成 trace 文件有三种方法：</p><ol><li>使用代码</li><li>使用 Android Studio</li><li>使用 DDMS</li></ol><h4 id="使用代码生成trace文件"><a href="#使用代码生成trace文件" class="headerlink" title="使用代码生成trace文件"></a>使用代码生成trace文件</h4><p>android.os.Debug类，其中重要的两个方法Debug.startMethodTracing()和Debug.stopMethodTracing()。这两个方法用来创建.trace文件，将从Debug.startMethodTracing()开始，到Debug.stopMethodTracing()结束，期间所有的调用过程保存在.trace文件中，包括调用的函数名称和执行的时间等信息。<br>    把下面代码分别在加在调试起始代码的位置，和终止位置。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Debug.startMethodTracing(Environment.getExternalStorageDirectory().getPath()+<span class="string">"/"</span>+TAG);    //开始 trace，保存文件到外部存储空间的TAG.trace，/mnt/sdcard目录下</span><br><span class="line">    // ...</span><br><span class="line">Debug.stopMethodTracing();    //结束</span><br></pre></td></tr></table></figure></p><p>代码很简单，当你调用开始代码的时候，系统会生产 trace 文件，并且产生追踪数据，当你调用结束代码时，会将追踪数据写入到 trace 文件中。</p><p>下一步使用 adb 命令将 trace 文件导出到电脑：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb pull /sdcard/TAG.trace /tmp</span><br></pre></td></tr></table></figure></p><p>使用代码生成 trace 方式的好处是容易控制追踪的开始和结束，缺点就是步骤稍微多了一点。</p><h4 id="使用Android-Studio生成trace文件"><a href="#使用Android-Studio生成trace文件" class="headerlink" title="使用Android Studio生成trace文件"></a>使用Android Studio生成trace文件</h4><p>暂时没有用过</p><h3 id="使用Android-Device-Monitor生成Trace文件"><a href="#使用Android-Device-Monitor生成Trace文件" class="headerlink" title="使用Android Device Monitor生成Trace文件"></a>使用Android Device Monitor生成Trace文件</h3><p>参考: <a href="https://blog.csdn.net/itfootball/article/details/48792435" target="_blank" rel="noopener">Android性能专项测试之TraceView工具(Device Monitor)</a></p><h2 id="根据-TraceView-显示内容定位问题"><a href="#根据-TraceView-显示内容定位问题" class="headerlink" title="根据 TraceView 显示内容定位问题"></a>根据 TraceView 显示内容定位问题</h2><p>定位问题时 TraceView 的使用方式：</p><ul><li>从上半部分查看哪些线程执行时间长？什么时候开始执行？与主线程交错时间？</li><li>哪些方法的执行需要花费很长时间点击 TraceView 中的 Cpu Time/Call，按照占用 CPU 时间从高到低排序</li><li>哪些方法调用次数非常频繁,点击 TraceView 中的 Calls + Recur Calls/Total ，按照调用次数从高到底排序排序后，然后逐个排查是否有项目代码或者依赖库代码，有的话点击查看详情，查看是这个方法还是调用的子方法的问题，进一步定位问题。</li></ul><h1 id="Systrace"><a href="#Systrace" class="headerlink" title="Systrace"></a>Systrace</h1><h2 id="Systrace简介"><a href="#Systrace简介" class="headerlink" title="Systrace简介"></a>Systrace简介</h2><p>Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统（如surfaceflinger、WindowManagerService等Framework部分关键模块、服务）的运行信息，从而帮助开发者更直观的分析系统瓶颈，改进性能。</p><p>Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中，它主要由3部分组成：</p><ul><li>内核部分：Systrace利用了Linux Kernel中的ftrace功能。所以，如果要使用Systrace的话，必须开启kernel中和ftrace相关的模块。</li><li>数据采集部分：Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时，Android还有一个atrace程序，它可以从ftrace中读取统计信息然后交给数据分析工具来处理。</li><li>数据分析工具：Android提供一个systrace.py（python脚本文件，位于Android SDK目录/platform-tools/systrace中，其内部将调用atrace程序）用来配置数据采集的方式（如采集数据的标签、输出文件名等）和收集ftrace统计数据并生成一个结果网页文件供用户查看。</li></ul><p>从本质上说，Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace。Android 4.1为系统中的几个关键进程和模块都添加了Systrace功能。以显示系统中重要模块Hwcomposer为例，其代码中使用Systrace的方法如图2-1所示：</p><h1 id="Oprofile"><a href="#Oprofile" class="headerlink" title="Oprofile"></a>Oprofile</h1><h1 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h1><p><a href="https://github.com/francistao/LearningNotes/blob/master/Part1/Android/Android%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E6%80%BB%E7%BB%93.md" target="_blank" rel="noopener">Android 内存泄漏总结</a><br><a href="http://drakeet.me/android-leaks/" target="_blank" rel="noopener">Android 内存泄漏案例和解析</a><br><a href="https://github.com/francistao/LearningNotes/blob/master/Part1/Android/Handler%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E5%88%86%E6%9E%90%E5%8F%8A%E8%A7%A3%E5%86%B3.md" target="_blank" rel="noopener">Handler内存泄漏分析及解决</a></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/innost/article/details/9008691" target="_blank" rel="noopener">Android系统性能调优工具介绍</a>  介绍了TraceView、Systrace、Oprofile等性能分析工具<br><a href="https://blog.csdn.net/cangely/article/details/80504292" target="_blank" rel="noopener">Android Vitals各性能指标介绍</a> Google官方的性能介绍<br><a href="https://blog.csdn.net/u011974987/article/details/51753774" target="_blank" rel="noopener">Android 性能指标检测工具</a><br><a href="http://www.cnblogs.com/sunzn/p/3192231.html" target="_blank" rel="noopener">Android 编程下的 TraceView 简介及其案例实战</a><br><a href="https://blog.csdn.net/u011240877/article/details/54347396" target="_blank" rel="noopener">Android 性能优化：使用 TraceView 找到卡顿的元凶</a><br><a href="https://juejin.im/post/5874bff0128fe1006b443fa0" target="_blank" rel="noopener">Android性能优化（一）之启动加速35%</a><br><a href="https://blog.csdn.net/u011240877/article/details/54141714" target="_blank" rel="noopener">Android 性能优化：使用 Lint 优化代码、去除多余资源</a><br><a href="https://blog.csdn.net/u011240877/article/details/53142177" target="_blank" rel="noopener">Android 性能优化：多线程</a></p><p><a href="https://race604.com/android-weekly-30/" target="_blank" rel="noopener">[第 30 期] Android 周报</a>  有些性能的文章</p><p><a href="https://blog.csdn.net/xiyangyang8/article/details/50545707" target="_blank" rel="noopener">Android性能分析工具Systrace和TraceView的使用</a></p><p><a href="https://juejin.im/post/5b50b017f265da0f7b2f649c?utm_source=gold_browser_extension" target="_blank" rel="noopener">Android 性能优化最佳实践</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;在软件开发过程中，想必很多读者都遇到过系统性能问题。而解决系统性能问题的几个主要步骤是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;测评：对系统进行大量有针
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="性能" scheme="http://yoursite.com/tags/%E6%80%A7%E8%83%BD/"/>
    
  </entry>
  
  <entry>
    <title>StrictMode 机制以及性能调优</title>
    <link href="http://yoursite.com/Android-StrictMode-Analysis/"/>
    <id>http://yoursite.com/Android-StrictMode-Analysis/</id>
    <published>2018-05-15T01:26:27.000Z</published>
    <updated>2018-08-07T06:54:50.787Z</updated>
    
    <content type="html"><![CDATA[<p>作为Android开发，日常的开发工作中或多或少要接触到性能问题，比如我的Android程序运行缓慢卡顿，并且常常出现ANR对话框等等问题。既然有性能问题，就需要进行性能优化。正所谓工欲善其事，必先利其器。一个好的工具，可以帮助我们发现并定位问题，进而有的放矢进行解决。本文主要介绍StrictMode 在Android 应用开发中的应用和一些问题。</p><p>Google 官方的StrictMode文档： <a href="https://developer.android.com/reference/android/os/StrictMode" target="_blank" rel="noopener">https://developer.android.com/reference/android/os/StrictMode</a></p><hr><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>StrictMode，严苛模式，是Android提供的一种运行时检测机制，用于检测代码运行时的一些不规范的操作，最常见的场景是用于发现主线程的IO操作和网络读写等耗时的操作。</p><p>StrictMode包含两个维度的概念：<br>• <strong>Policy(策略):</strong> 是指StrictMode对一些违规操作的发现策略，分为两类：<br>   一类是针对一个具体的线程(<strong>ThreadPolicy</strong>)。<br>   线程策略检测的内容有</p><ul><li>自定义的耗时调用 使用detectCustomSlowCalls()开启</li><li>磁盘读取操作 使用detectDiskReads()开启</li><li>磁盘写入操作 使用detectDiskWrites()开启</li><li><p>网络操作 使用detectNetwork()开启</p><p>另一类是针对虚拟机的所有对象(<strong>VMPolicy</strong>)。<br>虚拟机策略检测的内容有</p></li><li>Activity泄露 使用detectActivityLeaks()开启</li><li>未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启</li><li>泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启</li><li>检测实例数量 使用setClassInstanceLimit()开启</li></ul><p>• <strong>Penalty(惩罚)：</strong>是指StrictMode发现违规操作后进行惩罚的方式，譬如绘制红框、打印日志、显示对话框、杀掉进程等。</p><p>Android在很多关键的代码路径上都植入了StrictMode，譬如磁盘读写、网络访问、系统进程启动等。StrictMode会根据设置的策略进行检查，如果某个进程在代码运行时出现了违规操作，那么就会受到”惩罚”。</p><p>应用程序可以利用StrictMode尽可能的发现一些编码的疏漏， Android在 packages/experimental/StrictModeTest 这个APK中提供了常见违规操作的样例， 谨作为大家的反面教材。</p><p>本文深入分析StrictMode背后的实现原理以及使用场景。</p><h1 id="StrictMode机制"><a href="#StrictMode机制" class="headerlink" title="StrictMode机制"></a>StrictMode机制</h1><p>StrictMode的实现涉及到以下源码：<br>• <a href="https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/BlockGuard.java" target="_blank" rel="noopener">libcore/dalvik/src/main/java/dalvik/system/BlockGuard.java</a><br>• <a href="https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/CloseGuard.java" target="_blank" rel="noopener">libcore/dalvik/src/main/java/dalvik/system/CloseGuard.java</a><br>• <a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/StrictMode.java" target="_blank" rel="noopener">frameworks/base/core/java/android/os/StrictMode.java</a></p><ul><li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/StrictMode.aidl" target="_blank" rel="noopener">StrictMode.aidl</a></li></ul><p>总体而言，StrictMode机制所涉及到的代码量并不大，但Android中植入StrictMode的地方都是一些重要的关口，StrictMode所体现的面向接口编程的思想以及设计模式的应用，值得我们好好学习。 下面，我们就深入源码，分析一下StrictMode机制的内部实现。</p><h2 id="BlockGuard和CloseGuard"><a href="#BlockGuard和CloseGuard" class="headerlink" title="BlockGuard和CloseGuard"></a>BlockGuard和CloseGuard</h2><p>StrictMode针对单个线程和虚拟机的所有对象都定义了检查策略，用来发现一些违规操作，譬如：主线程中的磁盘读/写、网络访问、未关闭cursor，这些操作都能够被StrictMode检查出来。 怎么做到的呢？在做这些操作时，植入StrictMode的检查代码就可以了。有一部分植入代码是建立在BlockGuard和CloseGuard之上的，可以说，StrictMode是建立在BlockGuard和CloseGuard之上的机制。</p><p><strong>Guard</strong>有“守卫”的意思，<strong>Block</strong>是阻塞的意思，在进行一些耗时操作时，譬如磁盘读写、网络操作，有一个守卫在监测着，它就是BlockGuard，如果这些耗时的操作导致主线程阻塞，BlockGuard就会发出通知; <strong>Close</strong>对应到可打开的文件，在文件被打开后，也有一个守卫在监测着，它就是CloseGuard，如果没有关闭文件，则CloseGuard就会发出通知。</p><p>对应的来看一下 <a href="https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/BlockGuard.java" target="_blank" rel="noopener">BlockGuard.java</a>的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> dalvik.system;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.FileDescriptor;</span><br><span class="line"><span class="keyword">import</span> java.io.FileNotFoundException;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.math.BigInteger;</span><br><span class="line"><span class="keyword">import</span> java.net.SocketException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Mechanism to let threads set restrictions on what code is allowed</span></span><br><span class="line"><span class="comment"> * to do in their thread.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;This is meant for applications to prevent certain blocking</span></span><br><span class="line"><span class="comment"> * operations from running on their main event loop (or "UI") threads.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;Note that this is all best-effort to catch most accidental mistakes</span></span><br><span class="line"><span class="comment"> * and isn't intended to be a perfect mechanism, nor provide any sort of</span></span><br><span class="line"><span class="comment"> * security.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">BlockGuard</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// <span class="doctag">TODO:</span> refactor class name to something more generic, since its scope is</span></span><br><span class="line">    <span class="comment">// growing beyond just blocking/logging.</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DISALLOW_DISK_WRITE = <span class="number">0x01</span>;<span class="comment">//不允许磁盘盘写</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DISALLOW_DISK_READ = <span class="number">0x02</span>;<span class="comment">//不允许从磁盘读</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DISALLOW_NETWORK = <span class="number">0x04</span>;<span class="comment">//不允许有网络操作</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PASS_RESTRICTIONS_VIA_RPC = <span class="number">0x08</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PENALTY_LOG = <span class="number">0x10</span>;<span class="comment">//惩罚的log</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PENALTY_DIALOG = <span class="number">0x20</span>;<span class="comment">//惩罚的dialog</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PENALTY_DEATH = <span class="number">0x40</span>;<span class="comment">//惩罚kill</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Policy</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Called on disk writes.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onWriteToDisk</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Called on disk reads.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onReadFromDisk</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Called on network operations.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onNetwork</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Called on unbuffered input/ouput operations.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onUnbufferedIO</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Returns the policy bitmask, for shipping over Binder calls</span></span><br><span class="line"><span class="comment">         * to remote threads/processes and reinstantiating the policy</span></span><br><span class="line"><span class="comment">         * there.  The bits in the mask are from the DISALLOW_* and</span></span><br><span class="line"><span class="comment">         * PENALTY_* constants.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">int</span> <span class="title">getPolicyMask</span><span class="params">()</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">BlockGuardPolicyException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span> </span>&#123;</span><br><span class="line">        <span class="comment">// bitmask of DISALLOW_*, PENALTY_*, etc flags</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> mPolicyState;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> mPolicyViolated;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> String mMessage;   <span class="comment">// may be null</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">BlockGuardPolicyException</span><span class="params">(<span class="keyword">int</span> policyState, <span class="keyword">int</span> policyViolated)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>(policyState, policyViolated, <span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">BlockGuardPolicyException</span><span class="params">(<span class="keyword">int</span> policyState, <span class="keyword">int</span> policyViolated, String message)</span> </span>&#123;</span><br><span class="line">            mPolicyState = policyState;</span><br><span class="line">            mPolicyViolated = policyViolated;</span><br><span class="line">            mMessage = message;</span><br><span class="line">            fillInStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPolicy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> mPolicyState;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPolicyViolation</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> mPolicyViolated;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> String <span class="title">getMessage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Note: do not change this format casually.  It's</span></span><br><span class="line">            <span class="comment">// somewhat unfortunately Parceled and passed around</span></span><br><span class="line">            <span class="comment">// Binder calls and parsed back into an Exception by</span></span><br><span class="line">            <span class="comment">// Android's StrictMode.  This was the least invasive</span></span><br><span class="line">            <span class="comment">// option and avoided a gross mix of Java Serialization</span></span><br><span class="line">            <span class="comment">// combined with Parcels.</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">"policy="</span> + mPolicyState + <span class="string">" violation="</span> + mPolicyViolated +</span><br><span class="line">                    (mMessage == <span class="keyword">null</span> ? <span class="string">""</span> : (<span class="string">" msg="</span> + mMessage));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * The default, permissive policy that doesn't prevent any operations.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Policy LAX_POLICY = <span class="keyword">new</span> Policy() &#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onWriteToDisk</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReadFromDisk</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onNetwork</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onUnbufferedIO</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPolicyMask</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;Policy&gt; threadPolicy = <span class="keyword">new</span> ThreadLocal&lt;Policy&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span> <span class="function"><span class="keyword">protected</span> Policy <span class="title">initialValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> LAX_POLICY;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Get the current thread's policy.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the current thread's policy.  Never returns null.</span></span><br><span class="line"><span class="comment">     *     Will return the LAX_POLICY instance if nothing else is set.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Policy <span class="title">getThreadPolicy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> threadPolicy.get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Sets the current thread's block guard policy.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> policy policy to set.  May not be null.  Use the public LAX_POLICY</span></span><br><span class="line"><span class="comment">     *   if you want to unset the active policy.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setThreadPolicy</span><span class="params">(Policy policy)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (policy == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"policy == null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        threadPolicy.set(policy);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">BlockGuard</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>从BlockGuard的代码来看，很简单，就定义了一些类型和方法接口。</p><p>同样的看看<a href="https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/CloseGuard.java" target="_blank" rel="noopener">CloseGuard.java</a>方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> dalvik.system;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * CloseGuard is a mechanism for flagging implicit finalizer cleanup of</span></span><br><span class="line"><span class="comment"> * resources that should have been cleaned up by explicit close</span></span><br><span class="line"><span class="comment"> * methods (aka "explicit termination methods" in Effective Java).</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * A simple example: &lt;pre&gt;   &#123;<span class="doctag">@code</span></span></span><br><span class="line"><span class="comment"> *   class Foo &#123;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       private final CloseGuard guard = CloseGuard.get();</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       ...</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       public Foo() &#123;</span></span><br><span class="line"><span class="comment"> *           ...;</span></span><br><span class="line"><span class="comment"> *           guard.open("cleanup");</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       public void cleanup() &#123;</span></span><br><span class="line"><span class="comment"> *          guard.close();</span></span><br><span class="line"><span class="comment"> *          ...;</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       protected void finalize() throws Throwable &#123;</span></span><br><span class="line"><span class="comment"> *           try &#123;</span></span><br><span class="line"><span class="comment"> *               // Note that guard could be null if the constructor threw.</span></span><br><span class="line"><span class="comment"> *               if (guard != null) &#123;</span></span><br><span class="line"><span class="comment"> *                   guard.warnIfOpen();</span></span><br><span class="line"><span class="comment"> *               &#125;</span></span><br><span class="line"><span class="comment"> *               cleanup();</span></span><br><span class="line"><span class="comment"> *           &#125; finally &#123;</span></span><br><span class="line"><span class="comment"> *               super.finalize();</span></span><br><span class="line"><span class="comment"> *           &#125;</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *   &#125;</span></span><br><span class="line"><span class="comment"> * &#125;&lt;/pre&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * In usage where the resource to be explicitly cleaned up are</span></span><br><span class="line"><span class="comment"> * allocated after object construction, CloseGuard protection can</span></span><br><span class="line"><span class="comment"> * be deferred. For example: &lt;pre&gt;   &#123;<span class="doctag">@code</span></span></span><br><span class="line"><span class="comment"> *   class Bar &#123;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       private final CloseGuard guard = CloseGuard.get();</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       ...</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       public Bar() &#123;</span></span><br><span class="line"><span class="comment"> *           ...;</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       public void connect() &#123;</span></span><br><span class="line"><span class="comment"> *          ...;</span></span><br><span class="line"><span class="comment"> *          guard.open("cleanup");</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       public void cleanup() &#123;</span></span><br><span class="line"><span class="comment"> *          guard.close();</span></span><br><span class="line"><span class="comment"> *          ...;</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *       protected void finalize() throws Throwable &#123;</span></span><br><span class="line"><span class="comment"> *           try &#123;</span></span><br><span class="line"><span class="comment"> *               // Note that guard could be null if the constructor threw.</span></span><br><span class="line"><span class="comment"> *               if (guard != null) &#123;</span></span><br><span class="line"><span class="comment"> *                   guard.warnIfOpen();</span></span><br><span class="line"><span class="comment"> *               &#125;</span></span><br><span class="line"><span class="comment"> *               cleanup();</span></span><br><span class="line"><span class="comment"> *           &#125; finally &#123;</span></span><br><span class="line"><span class="comment"> *               super.finalize();</span></span><br><span class="line"><span class="comment"> *           &#125;</span></span><br><span class="line"><span class="comment"> *       &#125;</span></span><br><span class="line"><span class="comment"> *   &#125;</span></span><br><span class="line"><span class="comment"> * &#125;&lt;/pre&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * When used in a constructor calls to &#123;<span class="doctag">@code</span> open&#125; should occur at</span></span><br><span class="line"><span class="comment"> * the end of the constructor since an exception that would cause</span></span><br><span class="line"><span class="comment"> * abrupt termination of the constructor will mean that the user will</span></span><br><span class="line"><span class="comment"> * not have a reference to the object to cleanup explicitly. When used</span></span><br><span class="line"><span class="comment"> * in a method, the call to &#123;<span class="doctag">@code</span> open&#125; should occur just after</span></span><br><span class="line"><span class="comment"> * resource acquisition.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">CloseGuard</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Instance used when CloseGuard is disabled to avoid allocation.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> CloseGuard NOOP = <span class="keyword">new</span> CloseGuard();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Enabled by default so we can catch issues early in VM startup.</span></span><br><span class="line"><span class="comment">     * Note, however, that Android disables this early in its startup,</span></span><br><span class="line"><span class="comment">     * but enables it with DropBoxing for system apps on debug builds.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> ENABLED = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Hook for customizing how CloseGuard issues are reported.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> Reporter REPORTER = <span class="keyword">new</span> DefaultReporter();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * The default &#123;<span class="doctag">@link</span> Tracker&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> DefaultTracker DEFAULT_TRACKER = <span class="keyword">new</span> DefaultTracker();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Hook for customizing how CloseGuard issues are tracked.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> Tracker currentTracker = DEFAULT_TRACKER;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns a CloseGuard instance. If CloseGuard is enabled, &#123;<span class="doctag">@code</span></span></span><br><span class="line"><span class="comment">     * #open(String)&#125; can be used to set up the instance to warn on</span></span><br><span class="line"><span class="comment">     * failure to close. If CloseGuard is disabled, a non-null no-op</span></span><br><span class="line"><span class="comment">     * instance is returned.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> CloseGuard <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!ENABLED) &#123;</span><br><span class="line">            <span class="keyword">return</span> NOOP;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> CloseGuard();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Used to enable or disable CloseGuard. Note that CloseGuard only</span></span><br><span class="line"><span class="comment">     * warns if it is enabled for both allocation and finalization.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setEnabled</span><span class="params">(<span class="keyword">boolean</span> enabled)</span> </span>&#123;</span><br><span class="line">        ENABLED = enabled;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * True if CloseGuard mechanism is enabled.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">isEnabled</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ENABLED;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Used to replace default Reporter used to warn of CloseGuard</span></span><br><span class="line"><span class="comment">     * violations. Must be non-null.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setReporter</span><span class="params">(Reporter reporter)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (reporter == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"reporter == null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        REPORTER = reporter;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns non-null CloseGuard.Reporter.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Reporter <span class="title">getReporter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> REPORTER;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Sets the &#123;<span class="doctag">@link</span> Tracker&#125; that is notified when resources are allocated and released.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;This is only intended for use by &#123;<span class="doctag">@code</span> dalvik.system.CloseGuardSupport&#125; class and so</span></span><br><span class="line"><span class="comment">     * MUST NOT be used for any other purposes.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if tracker is null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setTracker</span><span class="params">(Tracker tracker)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (tracker == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"tracker == null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        currentTracker = tracker;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns &#123;<span class="doctag">@link</span> #setTracker(Tracker) last Tracker that was set&#125;, or otherwise a default</span></span><br><span class="line"><span class="comment">     * Tracker that does nothing.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;This is only intended for use by &#123;<span class="doctag">@code</span> dalvik.system.CloseGuardSupport&#125; class and so</span></span><br><span class="line"><span class="comment">     * MUST NOT be used for any other purposes.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Tracker <span class="title">getTracker</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> currentTracker;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">CloseGuard</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * If CloseGuard is enabled, &#123;<span class="doctag">@code</span> open&#125; initializes the instance</span></span><br><span class="line"><span class="comment">     * with a warning that the caller should have explicitly called the</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@code</span> closer&#125; method instead of relying on finalization.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> closer non-null name of explicit termination method</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if closer is null, regardless of</span></span><br><span class="line"><span class="comment">     * whether or not CloseGuard is enabled</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(String closer)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// always perform the check for valid API usage...</span></span><br><span class="line">        <span class="keyword">if</span> (closer == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"closer == null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// ...but avoid allocating an allocationSite if disabled</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span> == NOOP || !ENABLED) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        String message = <span class="string">"Explicit termination method '"</span> + closer + <span class="string">"' not called"</span>;</span><br><span class="line">        allocationSite = <span class="keyword">new</span> Throwable(message);</span><br><span class="line">        currentTracker.open(allocationSite);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Throwable allocationSite;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Marks this CloseGuard instance as closed to avoid warnings on</span></span><br><span class="line"><span class="comment">     * finalization.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        currentTracker.close(allocationSite);</span><br><span class="line">        allocationSite = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * If CloseGuard is enabled, logs a warning if the caller did not</span></span><br><span class="line"><span class="comment">     * properly cleanup by calling an explicit close method</span></span><br><span class="line"><span class="comment">     * before finalization. If CloseGuard is disabled, no action is</span></span><br><span class="line"><span class="comment">     * performed.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">warnIfOpen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (allocationSite == <span class="keyword">null</span> || !ENABLED) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        String message =</span><br><span class="line">                (<span class="string">"A resource was acquired at attached stack trace but never released. "</span></span><br><span class="line">                 + <span class="string">"See java.io.Closeable for information on avoiding resource leaks."</span>);</span><br><span class="line"></span><br><span class="line">        REPORTER.report(message, allocationSite);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Interface to allow customization of tracking behaviour.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;This is only intended for use by &#123;<span class="doctag">@code</span> dalvik.system.CloseGuardSupport&#125; class and so</span></span><br><span class="line"><span class="comment">     * MUST NOT be used for any other purposes.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Tracker</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">open</span><span class="params">(Throwable allocationSite)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">close</span><span class="params">(Throwable allocationSite)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Default tracker which does nothing special and simply leaves it up to the GC to detect a</span></span><br><span class="line"><span class="comment">     * leak.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultTracker</span> <span class="keyword">implements</span> <span class="title">Tracker</span> </span>&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Throwable allocationSite)</span> </span>&#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">(Throwable allocationSite)</span> </span>&#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Interface to allow customization of reporting behavior.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Reporter</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">report</span> <span class="params">(String message, Throwable allocationSite)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Default Reporter which reports CloseGuard violations to the log.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultReporter</span> <span class="keyword">implements</span> <span class="title">Reporter</span> </span>&#123;</span><br><span class="line">        <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">report</span> <span class="params">(String message, Throwable allocationSite)</span> </span>&#123;</span><br><span class="line">            System.logW(message, allocationSite);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Android在很多代码中植入了BlockGuard，以BlockGuardOs为例，这个类代理大部分POSIX系统调用接口，所谓代理，从代码角度，就是在一个类外层再做一层封装。 BlockGuardOs代理了Os类，并植入了BlockGuard，譬如BlockGuardOs.read()这个系统调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">(FileDescriptor fd, <span class="keyword">byte</span>[] bytes, <span class="keyword">int</span> byteOffset, <span class="keyword">int</span> byteCount)</span> <span class="keyword">throws</span> ErrnoException, InterruptedIOException </span>&#123;</span><br><span class="line">    BlockGuard.getThreadPolicy().onReadFromDisk();</span><br><span class="line">    <span class="keyword">return</span> os.read(fd, bytes, byteOffset, byteCount);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>经过BlockGuard的一层封装，在每次进行read()系统调用时，都会通过BlockGuard通知发生了读磁盘的操作：BlockGuard.getThreadPolicy().onReadFromDisk()<br>这里用到了BlockGuard的getThreadPolicy()方法，其实BlockGuard内部有一个Policy，定义了可能导致阻塞的方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Policy</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onWriteToDisk</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onReadFromDisk</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onNetwork</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">getPolicyMask</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这个Policy只是一个接口定义，专门暴露给外部的 ，StrictMode就实现了BlockGuard.Policy：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">AndroidBlockGuardPolicy</span> <span class="keyword">implements</span> <span class="title">BlockGuard</span>.<span class="title">Policy</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> mPolicyMask;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Map from violation stacktrace hashcode -&gt; uptimeMillis of</span></span><br><span class="line">    <span class="comment">// last violation.  No locking needed, as this is only</span></span><br><span class="line">    <span class="comment">// accessed by the same thread.</span></span><br><span class="line">    <span class="keyword">private</span> ArrayMap&lt;Integer, Long&gt; mLastViolationTime;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AndroidBlockGuardPolicy</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> policyMask)</span> </span>&#123;</span><br><span class="line">        mPolicyMask = policyMask;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"AndroidBlockGuardPolicy; mPolicyMask="</span> + mPolicyMask;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Part of BlockGuard.Policy interface:</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPolicyMask</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mPolicyMask;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Part of BlockGuard.Policy interface:</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onWriteToDisk</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> ((mPolicyMask &amp; DETECT_DISK_WRITE) == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (tooManyViolationsThisLoop()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        BlockGuard.BlockGuardPolicyException e = <span class="keyword">new</span> StrictModeDiskWriteViolation(mPolicyMask);</span><br><span class="line">        e.fillInStackTrace();</span><br><span class="line">        startHandlingViolationException(e);</span><br><span class="line">    &#125;</span><br><span class="line">    ....</span><br></pre></td></tr></table></figure></p><p>StrictMode不仅针对BlockGuard.Policy实现了自身的处理逻辑，还扩展了一个方法onCustomSlowCall()，通过BlockGuard.setThreadPolicy()就能够将AndroidBlockGuardPolicy植入到BlockGuard中。</p><p>再来看CloseGuard，与BlockGuard一样，Android在很多代码中也植入了CloseGuard，以FileInputStream为例：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FileInputStream</span> <span class="keyword">extends</span> <span class="title">InputStream</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1. 新建CloseGuard全局变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> CloseGuard guard = CloseGuard.get();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">FileInputStream</span><span class="params">(File file)</span> <span class="keyword">throws</span> FileNotFoundException </span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// 2. 设置CloseGuard标志</span></span><br><span class="line">        guard.open(<span class="string">"close"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 3. 清除CloseGuard标志</span></span><br><span class="line">        guard.close();</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">finalize</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 4. 判断Close标志是否被清除</span></span><br><span class="line">        <span class="keyword">if</span> (guard != <span class="keyword">null</span>) &#123;</span><br><span class="line">            guard.warnIfOpen();</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>CloseGuard的植入逻辑很清晰，一共分为4部分：</p><ul><li>1)新建一个CloseGuard全局变量</li><li>2)在对象初始化时，设置一个标志，表示需要调用close()方法关闭该对象</li><li>3)在关闭方法中，调用CloseGuard.close()方法，清除标志</li><li>4)在对象销毁时，调用CloaseGuard.warnIfOpen()方法，判断标志是否被清除：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">warnIfOpen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (allocationSite == <span class="keyword">null</span> || !ENABLED) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    String message =</span><br><span class="line">        (<span class="string">"A resource was acquired at attached stack trace but never released. "</span></span><br><span class="line">        + <span class="string">"See java.io.Closeable for information on avoiding resource leaks."</span>);</span><br><span class="line"></span><br><span class="line">    REPORTER.report(message, allocationSite);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>从CloseGuard.warnIfOpen()方法中，可以看到，设置的标志就是allocationSite变量，如果该变量已经置空了，表示已经被清除过了; 否则，就会通过REPORTER报告违规操作。</p><p>REPORTER是CloseGuard暴露一个接口，StrictMode就实现了这个接口：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">AndroidCloseGuardReporter</span> <span class="keyword">implements</span> <span class="title">CloseGuard</span>.<span class="title">Reporter</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">report</span> <span class="params">(String message, Throwable allocationSite)</span> </span>&#123;</span><br><span class="line">        onVmPolicyViolation(message, allocationSite);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>当StrictMode启用时，REPORTER就被设置成了AndroidCloseGuardReporter对象，如此一来，StrictMode就能够收集到CloseGuard报告的未关闭文件。</p><p>至此，我们揭开了StrictMode的面纱：Android通过BlockGuard和CloseGuard在一些执行路径中埋入了一些切点，譬如磁盘读写时BlockGuard会收到通知，对象销毁时CloseGuard就会收到通知。 BlockGuard和CloseGuard都设计了一套接口：BlockGuard.Policy和CloseGuard.Reporter，其实就是切点的不同分类，StrictMode正是利用这两个接口所定义的一些切点，切入了自已的处理逻辑。</p><blockquote><p>题外话: 得益于面向接口的设计，我们可以另起炉灶，完全独立于StrictMode再实现其他BlockGuard.Policy和CloseGuard.Reporter的处理逻辑; 也可以对BlockGuard.Policy和CloseGuard.Reporter进行扩展，StrictMode只需要实现新的处理逻辑即可，这都不会影响已有的架构。 接口定义和接口实现分离，两者可以独立变化，适应新的需求，这是桥接模式(Bridge Pattern)的精髓，它降低了Guard和StrictMode两者之间的耦合度。<br>从BlockGuardOs的设计中，我们也看到了代理模式(Proxy Pattern)，BlockGuardOs对被代理的Os类进行了简单控制，植入了BlockGuard的逻辑，作为一个中间者，处于调用者和被调用实体中间，能够降低两者的耦合度。</p></blockquote><h2 id="StrictMode-Policy"><a href="#StrictMode-Policy" class="headerlink" title="StrictMode Policy"></a>StrictMode Policy</h2><p>StrictMode利用了BlockGuard和CloseGuard，不仅实现了两者定义的一些策略(Policy)，还进行了扩展。 这些策略，在StrictMode看来，就是一些违规操作，下面我们深入介绍StrictMode定义的每一项违规操作。</p><h3 id="ThreadPolicy"><a href="#ThreadPolicy" class="headerlink" title="ThreadPolicy"></a>ThreadPolicy</h3><p>ThreadPolicy细分为以下几种：<br>• Disk Write：实现了BlockGuard的策略，写磁盘操作<br>• Disk Read：实现了BlockGuard的策略，读磁盘操作<br>• Network Access：实现了BlockGuard的策略，网络访问操作<br>• Custom Slow Code：StrictMode扩展的策略，目前只有Webview中植入了这项检查<br>前三项的植入都是通过BlockGuard完成的，StrictMode只是实现了处理逻辑;最后一项是StrictMode扩展的， 如果一个方法执行的时间较长，可以调用StrictMode.noteSlowCall()方法来发出通知。 当这些操作发生后，最终都会调用StrictMode.handleViolation()方法进行处理，后文再展开讨论这个方法。</p><p>StrictMode通过标志位来区别以上几项，为此还特意封装了一个内部类StrictMode.ThreadPlicy，目的是为了方便标志位的设定。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadPolicy</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ThreadPolicy标志位</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> mask;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">ThreadPolicy</span><span class="params">(<span class="keyword">int</span> mask)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.mask = mask;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 利用Builder完成标志位的初始化</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Builder</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> mMask = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Builder <span class="title">detectDiskReads</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> enable(DETECT_DISK_READ);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>ThreadPolicy的初始化采用了构建者模式(Builder Pattern)，这样一来，调用者在使用起来就会更加自然一点，不用记住各个标志位的意义。 为了完成标志位的设定，StrictMode提供setThreadPolicy()方法，接收ThreadPolicy类型的对象作为参数，该方法的实现就是直接调用setThreadPolicyMask()：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setThreadPolicy</span><span class="params">(<span class="keyword">final</span> ThreadPolicy policy)</span> </span>&#123;</span><br><span class="line">    setThreadPolicyMask(policy.mask);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">pivate <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setThreadPolicyMask</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> policyMask)</span> </span>&#123;</span><br><span class="line">    setBlockGuardPolicy(policyMask);</span><br><span class="line">    Binder.setThreadStrictModePolicy(policyMask);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这里完成了两个层面的ThreadPolicy设定：<br>• <strong>Java层</strong>，通过StrictMode.setBlockGuardPolicy()完成，最终会调用BlockGuard.setThreadPolicy()方法， 将AndroidBlockGuardPolicy对象设定为BlockGuard的Policy;</p><p>• <strong>Native层</strong>，通过Binder.setThreadStrictModePolicy()完成，看到这里，想必各位读者心中有了疑问，为什么还会有Native层的ThreadPolicy设置？ 其实，看到Binder，就很容易联想到这是用作跨进程调用的，当进程A发起跨进程调用进入到进程B后，那进程B中的违规操作怎么判定呢？当然也需要一个ThreadPolicy， Binder.setThreadStrictModePolicy()就是用来设置其他进程的ThreadPolicy。进程B中的违规异常也会通过Binder再传回进程A中，如此一来， 一个方法执行路径上的所有违规操作都会被StrictMode发现。</p><h3 id="VMPolicy"><a href="#VMPolicy" class="headerlink" title="VMPolicy"></a>VMPolicy</h3><p>ThreadPolicy主要用于发现一些容易导致主线程阻塞的操作，所以它针对的对象是单个线程; 而VMPolicy主要用于发现Java层的内存泄漏，所以它针对的是虚拟机的所有对象。 VMPolicy细分为以下几种：<br>• <strong>Cursor Leak：</strong> 如果注册SQlite Cursor后没有调用close()，则发生了泄漏。<br>• <strong>Closable Leak：</strong>这一项是CloseGuard的实现。如果存在未关闭的对象，则发生了泄漏。<br>• <strong>Activity Leak：</strong> 如果Activity在销毁后，其对象引用还被持有，则发生了泄漏。<br>• <strong>Instance Leak：</strong> StrictMode允许设置一个类的对象数量上限，在系统闲时，Strict会统计虚拟机中实际的对象数量，如果超出设定的上限，则判定为对象泄漏。<br>• <strong>Registion Leak：</strong> 如果注册IntentReceiver后没有调用unregisterReceiver()，则发生了泄漏<br>• <strong>File URI Exposure：</strong>这一项是安全性检查。通过file://的方式共享文件时，存在安全隐患。Android建议通过content://的方式共享文件。</p><p>如同ThreadPolicy一样，VMPolicy也采用了构建者模式(Builder Pattern)进行初始化，在Closable Leak这一项的使用上，与BlockGuard有异曲同工之妙， 但除了Closable Leak是利用CloseGuard以外，其他违规项都是StrictMode自身的逻辑，需要在一些关键路径上植入StrictMode的代码，我们举出两例：</p><p>例1：Cursor Leak</p><p>以下是SQLite Cursor植入了StrictMode机制的代码片段：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">SQLiteCursor</span><span class="params">(SQLiteCursorDriver driver, String editTable, SQLiteQuery query)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> (StrictMode.vmSqliteObjectLeaksEnabled()) &#123;</span><br><span class="line">        mStackTrace = <span class="keyword">new</span> DatabaseObjectNotClosedException().fillInStackTrace();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mStackTrace = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">finalize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// if the cursor hasn't been closed yet, close it first</span></span><br><span class="line">        <span class="keyword">if</span> (mWindow != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mStackTrace != <span class="keyword">null</span>) &#123;</span><br><span class="line">                String sql = mQuery.getSql();</span><br><span class="line">                <span class="keyword">int</span> len = sql.length();</span><br><span class="line">                StrictMode.onSqliteObjectLeaked(</span><br><span class="line">                    <span class="string">"Finalizing a Cursor that has not been deactivated or closed. "</span> +</span><br><span class="line">                    <span class="string">"database = "</span> + mQuery.getDatabase().getLabel() +</span><br><span class="line">                    <span class="string">", table = "</span> + mEditTable +</span><br><span class="line">                    <span class="string">", query = "</span> + sql.substring(<span class="number">0</span>, (len &gt; <span class="number">1000</span>) ? <span class="number">1000</span> : len),</span><br><span class="line">                    mStackTrace);</span><br><span class="line">            &#125;</span><br><span class="line">            close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.finalize();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在SQLiteCursor对象初始化时，设置一个变量mStackTrace，如果开启了DETECT_VM_CURSOR_LEAKS，则将其置为非空。 在SQLiteCursor对象销毁时，会对Cursor是否关闭进行判断(如果CursorWindow非空，则说明没有显示关闭Cursor)。此时，如果mStackTrace变量非空，则向StrictMode报告。</p><p>例2：Activity Leak</p><p>再来一例Activity植入StrictMode的逻辑：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ActivityThread.performLaunchActivity()</span><br><span class="line">└── StrictMode.incrementExpectedActivityCount()</span><br><span class="line"></span><br><span class="line">ActivityThread.performDestroyActivity()</span><br><span class="line">└── StrictMode.decrementExpectedActivityCount()</span><br></pre></td></tr></table></figure></p><p>StrictMode对象中维护了Activity的计数器，统计着Activity对象的数量。在Activity对象新建和销毁的时候，会分别调用increment和decrement，对计数进行增减调整。 每一次有Activity对象销毁，都会调用VMDebug.countInstancesOfClass()，计算虚拟机中实际的Activity对象数量，如果实际Activity对象的数量超出了StrictMode的统计值， 则说明Activity对象虽然销毁了，但其对象引用还在，这就存在泄漏。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">decrementExpectedActivityCount</span><span class="params">(Class klass)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">long</span> instances = VMDebug.countInstancesOfClass(klass, <span class="keyword">false</span>);</span><br><span class="line">    <span class="keyword">if</span> (instances &gt; limit) &#123;</span><br><span class="line">        Throwable tr = <span class="keyword">new</span> InstanceCountViolation(klass, instances, limit);</span><br><span class="line">        onVmPolicyViolation(tr.getMessage(), tr);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上述两例中，我们看到，虽然检测的形式各有不同，但本质都是在被检测的对象初始化时(constructor)设置一个标志，在对象销毁时(finalize)再对这个标志进行判断。其他的检测项的实现方式也都大同小异。</p><h2 id="StrictMode-Penalty"><a href="#StrictMode-Penalty" class="headerlink" title="StrictMode Penalty"></a>StrictMode Penalty</h2><p>当StrictMode发现有违规操作后，提供一些惩罚的方式，使用者可以自行组合。<br>• <strong>penaltyDialog：</strong> 弹出对话框<br>• <strong>penaltyDeath：</strong> 杀掉进程<br>• <strong>penaltyDeathOnNetwork</strong><br>• <strong>penaltyFlashScreen：</strong> 在屏幕的最外围绘制一个红框<br>• <strong>penaltyLog：</strong>打印StrictMode日志<br>• <strong>penaltyDropBox：</strong>将日志保存到Dropbox中</p><p>StrictMode内部是通过标志位来记录惩罚操作的类型的，并提供了上述的方法来设置不同的标志位。 StrictMode检测到违规操作后，最终都会调用StrictMode.handleViolation()方法，该方法中就会根据设置的标志位进行惩罚：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">handleViolation</span><span class="params">(<span class="keyword">final</span> ViolationInfo info)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">boolean</span> justDropBox = (info.policy &amp; THREAD_PENALTY_MASK) == PENALTY_DROPBOX;</span><br><span class="line">    <span class="keyword">if</span> (justDropBox) &#123;</span><br><span class="line">        dropboxViolationAsync(violationMaskSubset, info);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(</span><br><span class="line">                    RuntimeInit.getApplicationObject(),</span><br><span class="line">                    violationMaskSubset,</span><br><span class="line">                    info);</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> ((info.policy &amp; PENALTY_DEATH) != <span class="number">0</span>) &#123;</span><br><span class="line">        executeDeathPenalty(info);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>方法的实现逻辑一目了然，最终通过Binder发起跨进程调用，走到ActivityManagerService.handleApplicationStrictModeViolation()中.</p><h1 id="StrictMode使用"><a href="#StrictMode使用" class="headerlink" title="StrictMode使用"></a>StrictMode使用</h1><p>StrictMode机制只是用于发现一些违规操作，这些违规操作一般都是我们编码的疏漏，在运行时会被StrictMode暴露出来，但StrictMode并非真正意思上的“动态代码检查”。 各位读者有必要知道StrictMode的使用范围：</p><blockquote><p>StrictMode只是用在开发调试阶段，在正式发布时，应该关掉StrictMode,</p><blockquote><p>AOSP的源码中，USER版并没有打开StrictMode<br>由于Android还会对StrictMode的检查策略进行调整，所以Google Play建议上架的APK都关闭StrictMode; 从另一个角度，Google认为所有StrictMode的错误，在正式发布前，都应该解决。<br>StrictMode并不能发现Native层的违规操作，仅仅是用在Java层<br>StrictMode的使用场景可以分为三类，使用方式也都比较固定，可见StrictMode的对外接口还是封装得比较优美的。 下面，我们逐个介绍一下StrictMode的使用场景。</p></blockquote></blockquote><h2 id="普通应用开启StrictMode"><a href="#普通应用开启StrictMode" class="headerlink" title="普通应用开启StrictMode"></a>普通应用开启StrictMode</h2><p>对于应用程序而言，Android提供了一个最佳使用实践：尽可能早的在android.app.Application或android.app.Activity的生命周期使能StrictMode， onCreate()方法就是一个最佳的时机，越早开启就能在更多的代码执行路径上发现违规操作。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEVELOPER_MODE) &#123;</span><br><span class="line">       StrictMode.setThreadPolicy(<span class="keyword">new</span> StrictMode.ThreadPolicy.Builder()</span><br><span class="line">               .detectDiskReads()</span><br><span class="line">               .detectDiskWrites()</span><br><span class="line">               .detectNetwork()   <span class="comment">// or .detectAll() for all detectable problems</span></span><br><span class="line">               .penaltyLog()</span><br><span class="line">               .build());</span><br><span class="line">       StrictMode.setVmPolicy(<span class="keyword">new</span> StrictMode.VmPolicy.Builder()</span><br><span class="line">               .detectLeakedSqlLiteObjects()</span><br><span class="line">               .detectLeakedClosableObjects()</span><br><span class="line">               .penaltyLog()</span><br><span class="line">               .penaltyDeath()</span><br><span class="line">               .build());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">super</span>.onCreate();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>以上StrictMode的使能代码限定在DEVELOPER_MODE：<br>• 设定了Disk Read, Disk Write, Network Access三项ThreadPolicy，惩罚是打印日志;<br>• 设定了Cursor Leak, Closable Leak两项VMPolicy，惩罚是打印日志和杀掉进程。<br>当出现一些ThreadPolicy相关违规操作时，Android也提供了很多标准的解决方案，譬如Handler， AsyncTask， IntentService，能够将耗时的操作从主线程中分离出来。</p><h2 id="系统应用开启StrictMode"><a href="#系统应用开启StrictMode" class="headerlink" title="系统应用开启StrictMode"></a>系统应用开启StrictMode</h2><p>对于Android系统应用和系统进程(system_server)而言，其实默认就会开启StrictMode。 StrictMode提供了conditionallyEnableDebugLogging()方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">conditionallyEnableDebugLogging</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> doFlashes = SystemProperties.getBoolean(VISUAL_PROPERTY, <span class="keyword">false</span>)</span><br><span class="line">                &amp;&amp; !amTheSystemServerProcess();</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">boolean</span> suppress = SystemProperties.getBoolean(DISABLE_PROPERTY, <span class="keyword">false</span>);</span><br><span class="line">    <span class="keyword">if</span> (!doFlashes &amp;&amp; (IS_USER_BUILD || suppress)) &#123;</span><br><span class="line">        setCloseGuardEnabled(<span class="keyword">false</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> threadPolicyMask = StrictMode.DETECT_DISK_WRITE |</span><br><span class="line">            StrictMode.DETECT_DISK_READ |</span><br><span class="line">            StrictMode.DETECT_NETWORK;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> (!IS_USER_BUILD) &#123;</span><br><span class="line">        threadPolicyMask |= StrictMode.PENALTY_DROPBOX;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    StrictMode.setThreadPolicyMask(threadPolicyMask);</span><br><span class="line">    <span class="keyword">if</span> (IS_USER_BUILD) &#123;</span><br><span class="line">        setCloseGuardEnabled(<span class="keyword">false</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        VmPolicy.Builder policyBuilder = <span class="keyword">new</span> VmPolicy.Builder().detectAll().penaltyDropBox();</span><br><span class="line">        ...</span><br><span class="line">        setVmPolicy(policyBuilder.build());</span><br><span class="line">        setCloseGuardEnabled(vmClosableObjectLeaksEnabled());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>该方法的目的就是要设置ThreadPolicy和VMPolicy，不过会有一些条件判断，具体的逻辑不表。我们来看一下调用这个方法的地方：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">SystemServer.run()</span><br><span class="line">ServiceThread.run()</span><br><span class="line">ActivityThread.handleBindApplication()</span><br><span class="line">└── StrictMode.conditionallyEnableDebugLogging()</span><br></pre></td></tr></table></figure></p><p>这表示在system_server进程、一些全局的消息线程(IoThread, UiThread, FgThread, DisplayThread)、应用进程这些东西启动的时候开启StrictMode。 在ActivityThread.handleBindApplication()中有这么一段限制：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleBindApplication</span><span class="params">(AppBindData data)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> ((data.appInfo.flags &amp;</span><br><span class="line">         (ApplicationInfo.FLAG_SYSTEM |</span><br><span class="line">          ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != <span class="number">0</span>) &#123;</span><br><span class="line">        StrictMode.conditionallyEnableDebugLogging();</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>表示只为系统应用(FLAG_SYSTEM, FLAG_UPDATED_SYSTEM_APP)开启了StrictMode，其他应用还是需要自行开启。</p><h2 id="临时关闭StrictMode"><a href="#临时关闭StrictMode" class="headerlink" title="临时关闭StrictMode"></a>临时关闭StrictMode</h2><p>对于某些操作而言，我们明确知道是StrictMode定义的违规操作，但实际上对性能并没有什么影响，那么，在执行这类操作的时候，可以临时关闭StrictMode。 譬如针对一些主线程快速写磁盘的操作：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy();</span><br><span class="line">StrictMode.setThreadPolicy(<span class="keyword">new</span> StrictMode.ThreadPolicy.Builder(old)</span><br><span class="line">                                 .permitDiskWrites()</span><br><span class="line">                                 .build());</span><br><span class="line"><span class="comment">// 进行磁盘写操作...</span></span><br><span class="line">StrictMode.setThreadPolicy(old);</span><br></pre></td></tr></table></figure></p><p>首先，将旧的ThreadPolicy缓存一把; 然后，设置新的ThreadPolicy，并允许写磁盘操作; 最后，在进行完正常的写磁盘操作后，还原旧的ThreadPolicy。 这样就临时性的避开了StrictMode对写磁盘操作的检查。</p><h2 id="查看开启StrictMode的结果"><a href="#查看开启StrictMode的结果" class="headerlink" title="查看开启StrictMode的结果"></a>查看开启StrictMode的结果</h2><p>严格模式有很多种报告违例的形式，但是想要分析具体违例情况，还是需要查看日志，终端下过滤StrictMode就能得到违例的具体stacktrace信息。<br>adb logcat -b all | grep -rn StrictMode</p><h2 id="解决违例"><a href="#解决违例" class="headerlink" title="解决违例"></a>解决违例</h2><p>• 如果是主线程中出现文件读写违例，建议使用工作线程（必要时结合Handler）完成。<br>• 如果是对SharedPreferences写入操作，在API 9 以上 建议优先调用apply而非commit。<br>• 如果是存在未关闭的Closable对象，根据对应的stacktrace进行关闭。<br>• 如果是SQLite对象泄露，根据对应的stacktrace进行释放。</p><p>举个例子<br>以主线程中的文件写入为例，引起违例警告的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">writeToExternalStorage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    File externalStorage = Environment.getExternalStorageDirectory();</span><br><span class="line">    File destFile = <span class="keyword">new</span> File(externalStorage, <span class="string">"dest.txt"</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      OutputStream output = <span class="keyword">new</span> FileOutputStream(destFile, <span class="keyword">true</span>);</span><br><span class="line">        output.write(<span class="string">"droidyue.com"</span>.getBytes());</span><br><span class="line">        output.flush();</span><br><span class="line">        output.close();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>引起的警告为:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">D/StrictMode( <span class="number">9730</span>): StrictMode policy violation; ~duration=<span class="number">20</span> ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=<span class="number">31</span> violation=<span class="number">2</span></span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:<span class="number">1176</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:<span class="number">106</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at libcore.io.IoBridge.open(IoBridge.java:<span class="number">390</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at java.io.FileOutputStream.&lt;init&gt;(FileOutputStream.java:<span class="number">88</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at com.example.strictmodedemo.MainActivity.writeToExternalStorage(MainActivity.java:<span class="number">56</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at com.example.strictmodedemo.MainActivity.onCreate(MainActivity.java:<span class="number">30</span>)</span><br><span class="line">D/StrictMode( <span class="number">9730</span>):    at android.app.Activity.performCreate(Activity.java:<span class="number">4543</span>)</span><br></pre></td></tr></table></figure></p><p>因为上述属于主线程中的IO违例，解决方法就是讲写入操作放入工作线程。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">writeToExternalStorage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">          <span class="keyword">super</span>.run();</span><br><span class="line">          File externalStorage = Environment.getExternalStorageDirectory();</span><br><span class="line">          File destFile = <span class="keyword">new</span> File(externalStorage, <span class="string">"dest.txt"</span>);</span><br><span class="line">          <span class="keyword">try</span> &#123;</span><br><span class="line">              OutputStream output = <span class="keyword">new</span> FileOutputStream(destFile, <span class="keyword">true</span>);</span><br><span class="line">              output.write(<span class="string">"droidyue.com"</span>.getBytes());</span><br><span class="line">              output.flush();</span><br><span class="line">              output.close();</span><br><span class="line">          &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line">              e.printStackTrace();</span><br><span class="line">          &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">              e.printStackTrace();</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      &#125;.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然而这并非完善，因为OutputStream.write方法可能抛出IOException，导致存在OutputStream对象未关闭的情况，仍然需要改进避免出现Closable对象未关闭的违例。改进如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">writeToExternalStorage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">          <span class="keyword">super</span>.run();</span><br><span class="line">            File externalStorage = Environment.getExternalStorageDirectory();</span><br><span class="line">            File destFile = <span class="keyword">new</span> File(externalStorage, <span class="string">"dest.txt"</span>);</span><br><span class="line">            OutputStream output = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                output = <span class="keyword">new</span> FileOutputStream(destFile, <span class="keyword">true</span>);</span><br><span class="line">                output.write(<span class="string">"droidyue.com"</span>.getBytes());</span><br><span class="line">                output.flush();</span><br><span class="line">                output.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">null</span> != output) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                      output.close();</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="使用StrictMode检测内存泄露"><a href="#使用StrictMode检测内存泄露" class="headerlink" title="使用StrictMode检测内存泄露"></a>使用StrictMode检测内存泄露</h2><p>通常情况下，检测内存泄露，我们需要使用MAT对heap dump 文件进行分析，这种操作不困难，但也不容易。使用严格模式，只需要过滤日志就能发现内存泄露。</p><p>这里以Activity为例说明，首先我们需要开启对检测Activity泄露的违例检测。使用上面的detectAll或者detectActivityLeaks()均可。其次写一段能够产生Activity泄露的代码。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LeakyActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        MyApplication.sLeakyActivities.add(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>MyApplication中关于sLeakyActivities的部分实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyApplication</span> <span class="keyword">extends</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> IS_DEBUG = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ArrayList&lt;Activity&gt; sLeakyActivities = <span class="keyword">new</span> ArrayList&lt;Activity&gt;();</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>当我们反复进入LeakyActivity再退出，过滤StrictMode就会得到这样的日志:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">E/StrictMode( <span class="number">2622</span>): <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">strictmodedemo</span>.<span class="title">LeakyActivity</span></span>; instances=<span class="number">2</span>; limit=<span class="number">1</span></span><br><span class="line">E/StrictMode( <span class="number">2622</span>): android.os.StrictMode$InstanceCountViolation: <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">strictmodedemo</span>.<span class="title">LeakyActivity</span></span>; instances=<span class="number">2</span>; limit=<span class="number">1</span></span><br><span class="line">E/StrictMode( <span class="number">2622</span>):    at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:<span class="number">1</span>)</span><br></pre></td></tr></table></figure></p><p>分析日志，LeakyActivity本应该是只存在一份实例，但现在出现了2个，说明LeakyActivity发生了内存泄露。</p><p>严格模式除了可以检测Activity的内存泄露之外，还能自定义检测类的实例泄露。从API 11 开始，系统提供的这个方法可以实现我们的需求。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> StrictMode.VmPolicy.<span class="function">Builder <span class="title">setClassInstanceLimit</span> <span class="params">(Class klass, <span class="keyword">int</span> instanceLimit)</span></span></span><br></pre></td></tr></table></figure><p>举个栗子，比如一个浏览器中只允许存在一个SearchBox实例，我们就可以这样设置已检测SearchBox实例的泄露<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">StrictMode.setVmPolicy(<span class="keyword">new</span> VmPolicy.Builder().setClassInstanceLimit(SearchBox.class, <span class="number">1</span>).penaltyLog().build());</span><br></pre></td></tr></table></figure></p><h2 id="自定义-noteSlowCall"><a href="#自定义-noteSlowCall" class="headerlink" title="自定义 noteSlowCall"></a>自定义 noteSlowCall</h2><p>StrictMode从 API 11开始允许开发者自定义一些耗时调用违例，这种自定义适用于自定义的任务执行类中，比如我们有一个进行任务处理的类，为TaskExecutor。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TaskExecutor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable task)</span> </span>&#123;</span><br><span class="line">        task.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>先需要跟踪每个任务的耗时情况，如果大于500毫秒需要提示给开发者，noteSlowCall就可以实现这个功能，如下修改代码:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TaskExecutor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">long</span> SLOW_CALL_THRESHOLD = <span class="number">500</span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">executeTask</span><span class="params">(Runnable task)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> startTime = SystemClock.uptimeMillis();</span><br><span class="line">        task.run();</span><br><span class="line">        <span class="keyword">long</span> cost = SystemClock.uptimeMillis() - startTime;</span><br><span class="line">        <span class="keyword">if</span> (cost &gt; SLOW_CALL_THRESHOLD) &#123;</span><br><span class="line">            StrictMode.noteSlowCall(<span class="string">"slowCall cost="</span> + cost);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>执行一个耗时2000毫秒的任务<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">TaskExecutor executor = <span class="keyword">new</span> TaskExecutor();</span><br><span class="line">executor.executeTask(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          Thread.sleep(<span class="number">2000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p> 得到的违例日志，注意其中~duration=20 ms并非耗时任务的执行时间，而我们的自定义信息msg=slowCall cost=2000才包含了真正的耗时。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">D/StrictMode(<span class="number">23890</span>): StrictMode policy violation; ~duration=<span class="number">20</span> ms: android.os.StrictMode$StrictModeCustomViolation: policy=<span class="number">31</span> violation=<span class="number">8</span> msg=slowCall cost=<span class="number">2000</span></span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.os.StrictMode$AndroidBlockGuardPolicy.onCustomSlowCall(StrictMode.java:<span class="number">1163</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.os.StrictMode.noteSlowCall(StrictMode.java:<span class="number">1974</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at com.example.strictmodedemo.TaskExecutor.executeTask(TaskExecutor.java:<span class="number">17</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at com.example.strictmodedemo.MainActivity.onCreate(MainActivity.java:<span class="number">36</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.Activity.performCreate(Activity.java:<span class="number">4543</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:<span class="number">1071</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:<span class="number">2158</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:<span class="number">2237</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.ActivityThread.access$<span class="number">600</span>(ActivityThread.java:<span class="number">139</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:<span class="number">1262</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.os.Handler.dispatchMessage(Handler.java:<span class="number">99</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.os.Looper.loop(Looper.java:<span class="number">156</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at android.app.ActivityThread.main(ActivityThread.java:<span class="number">5005</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at java.lang.reflect.Method.invokeNative(Native Method)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at java.lang.reflect.Method.invoke(Method.java:<span class="number">511</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:<span class="number">784</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">551</span>)</span><br><span class="line">D/StrictMode(<span class="number">23890</span>):    at dalvik.system.NativeStart.main(Native Method)</span><br></pre></td></tr></table></figure><h2 id="其他技巧"><a href="#其他技巧" class="headerlink" title="其他技巧"></a>其他技巧</h2><p>除了通过日志查看之外，我们也可以在开发者选项中开启严格模式，开启之后，如果主线程中有执行时间长的操作，屏幕则会闪烁，这是一个更加直接的方法。</p><p><strong>注意</strong></p><blockquote><p>在线上环境即Release版本不建议开启严格模式。<br>严格模式无法监控JNI中的磁盘IO和网络请求。<br>应用中并非需要解决全部的违例情况，比如有些IO操作必须在主线程中进行。</p></blockquote><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><blockquote><ol><li><a href="http://duanqz.github.io/2015-11-04-StrictMode-Analysis" target="_blank" rel="noopener">StrictMode机制以及使用场景</a></li><li><a href="https://www.cnblogs.com/yaowen/p/6024690.html" target="_blank" rel="noopener">Android严苛模式StrictMode使用详解</a> </li><li><a href="https://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/index.html" target="_blank" rel="noopener">Android性能调优利器StrictMode</a></li></ol></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;作为Android开发，日常的开发工作中或多或少要接触到性能问题，比如我的Android程序运行缓慢卡顿，并且常常出现ANR对话框等等问题。既然有性能问题，就需要进行性能优化。正所谓工欲善其事，必先利其器。一个好的工具，可以帮助我们发现并定位问题，进而有的放矢进行解决。本文
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="StrictMode" scheme="http://yoursite.com/categories/Android/StrictMode/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="StrictMode" scheme="http://yoursite.com/tags/StrictMode/"/>
    
  </entry>
  
  <entry>
    <title>Android中Keyguard解析</title>
    <link href="http://yoursite.com/Android-Keyguard-Analysis/"/>
    <id>http://yoursite.com/Android-Keyguard-Analysis/</id>
    <published>2018-05-04T16:00:00.000Z</published>
    <updated>2018-09-04T03:11:50.487Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p> 本文是基于Android版本：8.1.0来讨论Keyguard相关代码.</p><p> 在Android 8.1.0版本中，将Keyguard模块的代码完全移到了SystemUI，将其和SystemUI进行了合并。那么为何要把SystemUI和Keyguard代码放在一起呢？估计基于以下几点考虑：<br> a) 虽然在之前的版本中，Keyguard模块和SystemUI模块的代码是分开放在两个目录，且单独参与编译，Keyguard模块编译生成keyguard.jar，SystemUI模块单独编译生成SystemUI.apk,且SystemUI.apk包含了Keyguard.jar. 两个模块都是公用一个进程com.android.systemui.<br> b) 两个模块有公用的小模块，比如StatusBar、NotificationView等等.<br> c) 两个模块都需要对滑动事件进行处理，而且大部分的处理逻辑很像.</p><p> Keyguard模块的重点是：<br> • 事件的处理<br> • LockPatternUtils<br> • 锁屏界面的显示<br> • 锁屏界面的隐藏<br> • 密码的认证和校验<br> •<br> • </p><p> <img src="/Android-Keyguard-Analysis/Keyguard-FW.jpg" alt="&quot;Android4.4 Keyguard架构&quot;"></p><h1 id="Keyguard相关文件功能预分析"><a href="#Keyguard相关文件功能预分析" class="headerlink" title="Keyguard相关文件功能预分析"></a>Keyguard相关文件功能预分析</h1><p> 与Keyguard相关的代码有如下这些：<br> 从最重要的framework中相关模块开始分析,对于每个模块里面的每个块代码的初步分析顺序并不是按照其重要性来的，是按照每个模块里面相关类的字母顺序来讲解.<br> 在这个部分，我们主要任务是了解每个文件的功能和作用，因此，只是局限于单个文件眼前的功能，对Keyguard整体功能，并不能全面的理解.</p><h2 id="core模块代码"><a href="#core模块代码" class="headerlink" title="core模块代码"></a>core模块代码</h2><ol><li>framework/base/core模块有KeyguardManager,LockPatternUtils,<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">framework/base/core/java/android/app/KeyguardManager.java</span><br><span class="line">framework/base/core/java/com/android/internal/widget/LockPatternUtils.java</span><br><span class="line">frameworks/base/core/java/android/app/admin/DevicePolicyManager.java</span><br><span class="line">frameworks/base/core/java/android/os/TokenWatcher.java</span><br></pre></td></tr></table></figure></li></ol><p> 首先要说明一下，从Android 6.0 开始屏幕解锁(即除了指纹解锁和面部解锁的PIN码、密码、图案解锁)的认证部分(Credential)，都是通过Confirm Credential获取key的方式来完成,</p><p> <strong>KeyguardManager:</strong> 我们先来看看官方的注释<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class that can be used to lock and unlock the keyboard. The actual <span class="class"><span class="keyword">class</span> <span class="title">to</span> <span class="title">control</span> <span class="title">the</span> <span class="title">keyboard</span> <span class="title">locking</span> <span class="title">is</span> </span>&#123;<span class="meta">@link</span> android.app.KeyguardManager.KeyguardLock&#125;.</span><br></pre></td></tr></table></figure></p><p> 用于解锁屏，实际控制解锁的是其内部类KeyguardLock.接下来看看KeyguardManager有哪些实现.<br> • createConfirmDeviceCredentialIntent，有三个此方法的重载，分别创建<br>   a)ACTION_CONFIRM_DEVICE_CREDENTIAL 常规intent<br>   b)ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER 带用户ID的intent<br>   c)ACTION_CONFIRM_FRP_CREDENTIAL 恢复出厂设置的FRP的intent</p><p> • 内部类KeyguardLock,看看KeyguardLock的代码发现，只有两个函数，如下：<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@deprecated</span> Use &#123;<span class="doctag">@link</span> LayoutParams#FLAG_DISMISS_KEYGUARD&#125;</span></span><br><span class="line"><span class="comment"> * and/or &#123;<span class="doctag">@link</span> LayoutParams#FLAG_SHOW_WHEN_LOCKED&#125;</span></span><br><span class="line"><span class="comment"> * instead; this allows you to seamlessly hide the keyguard as your application</span></span><br><span class="line"><span class="comment"> * moves in and out of the foreground and does not require that any special</span></span><br><span class="line"><span class="comment"> * permissions be requested.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Handle returned by &#123;<span class="doctag">@link</span> KeyguardManager#newKeyguardLock&#125; that allows</span></span><br><span class="line"><span class="comment"> * you to disable / reenable the keyguard.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Deprecated</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">KeyguardLock</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> IBinder mToken = <span class="keyword">new</span> Binder();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String mTag;</span><br><span class="line"></span><br><span class="line">    KeyguardLock(String tag) &#123;</span><br><span class="line">        mTag = tag;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Disable the keyguard from showing.  If the keyguard is currently</span></span><br><span class="line"><span class="comment">     * showing, hide it.  The keyguard will be prevented from showing again</span></span><br><span class="line"><span class="comment">     * until &#123;<span class="doctag">@link</span> #reenableKeyguard()&#125; is called.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * A good place to call this is from &#123;<span class="doctag">@link</span> android.app.Activity#onResume()&#125;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Note: This call has no effect while any &#123;<span class="doctag">@link</span> android.app.admin.DevicePolicyManager&#125;</span></span><br><span class="line"><span class="comment">     * is enabled that requires a password.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> #reenableKeyguard()</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RequiresPermission</span>(Manifest.permission.DISABLE_KEYGUARD)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">disableKeyguard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            mWM.disableKeyguard(mToken, mTag);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException ex) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Reenable the keyguard.  The keyguard will reappear if the previous</span></span><br><span class="line"><span class="comment">     * call to &#123;<span class="doctag">@link</span> #disableKeyguard()&#125; caused it to be hidden.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * A good place to call this is from &#123;<span class="doctag">@link</span> android.app.Activity#onPause()&#125;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Note: This call has no effect while any &#123;<span class="doctag">@link</span> android.app.admin.DevicePolicyManager&#125;</span></span><br><span class="line"><span class="comment">     * is enabled that requires a password.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> #disableKeyguard()</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RequiresPermission</span>(Manifest.permission.DISABLE_KEYGUARD)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reenableKeyguard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            mWM.reenableKeyguard(mToken);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException ex) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>  我们看到在KeyguardLock类头部有个注解@deprecated,表明已经过时了，被弃用了.这种之前的Keyguard显示与隐藏，留给后面来跟踪.  </p><p> • KeyguardDismissCallback 内部抽象类<br> • isKeyguardLocked() 判断当前是否处于锁屏状态，调用的是WindowManagerService的isKeyguardLocked()方法.继续调用 WindowManagerPolicy，最后调到PhoneWindowManager里面,<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/** &#123;<span class="doctag">@inheritDoc</span>&#125; */</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isKeyguardLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> keyguardOn();</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">keyguardOn</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> 接下来看看KeyguardLock内部类,</p><p> <strong>TokenWatcher:</strong> 抽象类，定义</p><h2 id="services模块代码"><a href="#services模块代码" class="headerlink" title="services模块代码"></a>services模块代码</h2><ol start="2"><li>framework/base/services模块有PhoneWindowManager,KeyguardServiceDelegate,KeyguardServiceWrapper,KeyguardStateMonitor<br>PhoneWindowManager.java<br>KeyguardServiceDelegate  Keyguard服务代理<br>KeyguardServiceWrapper  Keyguard服务封装<br>KeyguardStateMonitor Keygurad状态监视<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java</span><br><span class="line">frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java</span><br><span class="line">frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java</span><br><span class="line">frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java</span><br><span class="line">frameworks/base/services/core/java/com/android/server/wm/KeyguardDisableHandler.java</span><br><span class="line">frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java</span><br></pre></td></tr></table></figure></li></ol><h2 id="SystemUI模块代码"><a href="#SystemUI模块代码" class="headerlink" title="SystemUI模块代码"></a>SystemUI模块代码</h2><ol start="3"><li><p>SystemUI中的Keyguard模块代码在frameworks/base/packages/SystemUI/src/com/android/systemui/目录,其中处理Keyguard逻辑相关的代码有：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>KeyguardHostView  即为最终需要展示界面的基类<br>KeyguardAbsKeyInputView<br>作为基类实现了key input password类型(PIN, Sim PIN, Sim PUK, password)的大多数公用函数。<br>KeyguardUpdateMonitor 注册了绝大多数的广播，负责处理界面的一些刷新流程处理<br>使用了观察者模式<br>KeyguardUpdateMonitorCallback<br>KeyguardDisplayManager  作为中间类去控制keyguard的show与hide,主要用于双屏异显<br>packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java<br>此类中涉及到两个重要的类<br>MediaRouter<br>Presentation 这个类是用来控制双屏异显的<br>LiftToActivateListener.java<br>AccessibilityService  无障碍/辅助功能</p></li></ol><p>KeyguardViewMediator 是整个待机解/锁屏业务的调度器，负责调度锁屏界面的相关动作及查询解锁屏状态。是一个服务<br>packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java</p><p>KeyguardService.java  KeyguardViewMediator为其子类</p><p>KeyguardBouncer 管理是非滑动解锁还是滑动解锁，控制安全锁屏的显示与隐藏<br>packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java</p><h2 id="SystemUI中Keyguard模块代码"><a href="#SystemUI中Keyguard模块代码" class="headerlink" title="SystemUI中Keyguard模块代码"></a>SystemUI中Keyguard模块代码</h2><ol start="4"><li><p>SystemUI中的Keyguard模块中处理Keyguard的UI显示相关的代码有：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedImageButton.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedLinearLayout.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedRelativeLayout.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/CarrierText.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/NumPadKey.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java</span><br><span class="line">frameworks/base/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java</span><br></pre></td></tr></table></figure><p><strong>AlphaOptimizedImageButton：</strong>A frame layout which does not have overlapping renderings commands and therefore does not need a layer when alpha is changed. 用于PIN码解锁时，确认键Button时使用.</p><p><strong>AlphaOptimizedLinearLayout:</strong>A linear layout which does not have overlapping renderings commands and therefore does not need a layer when alpha is changed. 一个LinearLayout，但是hasOverlappingRendering方法返回为false，主要目的是防止过度绘制，提升性能. 关于<a href="https://blog.csdn.net/xingchenxuanfeng/article/details/56488045" target="_blank" rel="noopener">hasOverlappingRendering方法请参考这里</a></p><p><strong>AlphaOptimizedRelativeLayout:</strong>同样的是一个相对布局的优化类.</p><p><strong>CarrierText:</strong> 继承自TextView，主要用来在锁屏界面，顶部StatusBar里面显示运营商信息以及当前的SIM卡状态和网络状态.</p><p><strong>EmergencyButton：</strong> 继承自Button， 锁屏界面的紧急拨号按钮</p><p><strong>EmergencyCarrierArea：</strong> 继承自AlphaOptimizedLinearLayout，主要处理紧急拨号按钮和这里的CarrierText的显示问题</p><p><strong>KeyguardAbsKeyInputView：</strong> 抽象类，继承自LinearLayout，实现了KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback 三个类，主要的功能是：Base class for PIN and password unlock screens. 即 PIN码和密码解锁的基类,实现了对输入的密码处理的主要的三个函数verifyPasswordAndUnlock(), onPasswordChecked(), handleAttemptLockout().</p><p><strong>KeyguardClockAccessibilityDelegate:</strong>Replaces fancy colons with regular colons. Only works on TextViews.重新定义了一个冒号，继承自View.AccessibilityDelegate，主要用于锁屏界面时钟显示时的冒号</p><p><strong>KeyguardConstants：</strong> 定义Keyguard模块用到的三个Debug开关</p><p><strong>KeyguardDisplayManager：</strong>Support multiple external displays，支持多个扩展设备上显示. 控制Keyguard的显示和隐藏，主要有三个函数show(),hide(),updatedisplay(),还有一个KeyguardPresentation类，继承自Presentation，Presentation类就是Android支持双屏异显的类。还有一个MediaRouter.SimpleCallback，用于监视外接设备。</p><p><strong>KeyguardEsimArea：</strong>This button is used by the device with embedded SIM card to disable current carrier to unlock the device with no cellular service.<br>继承自Button，按钮的主要作用是在eSIM卡(eSIM卡比日常见到的SIM卡基于硬件芯片的模块，安全级别可以做到最高，具体可以查下资料)的PIN码和PUK码解锁的时候,在无网络的情况下，diable掉carrier实现解锁设备.(eSIM卡的知识不懂，为啥要这样解锁设备，还不清楚)</p><p><strong>KeyguardHostView:</strong> 继承自FrameLayout，实现了SecurityCallback接口.Base class for keyguard view.  {@link #reset} is where you should reset the state of your view.  Use the {@link KeyguardViewCallback} via {@link #getCallback()} to send information back (such as poking the wake lock,or finishing the keyguard). Handles intercepting of media keys that still work when the keyguard is showing.是整个Keyguard显示的基类，可以reset(重置)Keyguard的显示，可以使用KeyguardViewCallback回调的方式，发信息  .在锁屏界面对多媒体相关的按键事件，比如播放音乐、暂停音乐、静音、音量+、音量-、耳机插入、上一首、下一首等事件进行拦截以及处理.<br>SecurityCallback接口定义了四个函数,具体代码见com.android.keyguard.KeyguardSecurityContainer.SecurityCallback,分别是:<br>• dismiss();<br>• userActivity();<br>• onSecurityModeChanged();<br>• finish();<br>• reset();</p><p><strong>KeyguardMessageArea:</strong> 继承自TextView，实现SecurityMessageDisplay.PIN码/图案/密码解锁界面时显示输入密码错误，以及输入次数等消息.在KeyguardPatternView中有使用.</p><p><strong>KeyguardPasswordView:</strong>Displays an alphanumeric (latin-1) key entry for the user to enter an unlock password.显示一个文本框，用来处理用户输入的密码字符，这里要讲一下锁屏界面的密码和PIN码(这里的PIN码不是SIM卡的PIN码)，两者的区别是，密码可以是字母和数字的组合，但是PIN码只能是由数字组成. 继承了KeyguardAbsKeyInputView实现了 KeyguardSecurityView, OnEditorActionListener, TextWatcher. 此类完成了用户进入密码解锁界面后，密码输入，密码错误时的提示信息处理、密码校验等事情.</p><p><strong>KeyguardPatternView:</strong> 继承自LinearLayout，实现了KeyguardSecurityView, AppearAnimationCreator&lt;LockPatternView.CellState&gt;,        EmergencyButton.EmergencyButtonCallback 接口,这里不是图案解锁中九宫格的View实现，九宫格的图案解锁实现是在com.android.internal.widget.LockPatternView中.<br>此类中，完成了用户进入图案解锁界面后，图案密码输入、图案密码错误时的提示信息处理、密码校验等事情.</p><p><strong>KeyguardPinBasedInputView：</strong>A Pin based Keyguard input view.继承自KeyguardAbsKeyInputView，实现了View.OnKeyListener, View.OnTouchListener接口.是一个PIN码输入的基类，因为总共有三类PIN码，a)正常PIN码,b)SIM卡的PIN码,c)SIM卡的PUK码三种.由于PIN码全部是数字组成，所以在PIN输入的时候，可以不用调用输入法的输入面板，在此类中，实现了数字输入面板的处理逻辑.</p><p><strong>KeyguardPINView：</strong>Displays a PIN pad for unlocking.继承自KeyguardPinBasedInputView,显示一个PIN码解锁的界面.  </p><p><strong>KeyguardSecurityCallback:</strong> 一个接口，定义了5个方法.<br>• dismiss() //Dismiss the given security screen.<br>• userActivity() //Manually report user activity to keep the device awake.<br>• isVerifyUnlockOnly() //Checks if keyguard is in “verify credentials” mode.<br>• reportUnlockAttempt(int userId, boolean success, int timeoutMs) //Call to report an unlock attempt.<br>• reset() //Resets the keyguard view.<br>这个接口和上面提到的SecurityCallback接口的区别在哪里呢？</p><p><strong>KeyguardSecurityContainer:</strong> 继承自FrameLayout,实现了如下功能：<br>• 实现了KeyguardSecurityView接口<br>• 定义了SecurityCallback接口.定义SecurityCallback接接口的作用是// Used to notify the container when something interesting happens.<br>• 实现了showTimeoutDialog()函数，密码输入错误多次后的弹框提示<br>• showAlmostAtWipeDialog()和showWipeDialog()函数，密码输入太多次后，擦除cache数据的提示<br>• reportFailedUnlockAttempt()函数，上报错误次数<br>• KeyguardSecurityCallback 接口的实现，整个Keyguard里面，KeyguardSecurityCallback接口都是直接当内部类的方式new出来的，具体实现，就在KeyguardSecurityContainer里面，从KeyguardSecurityCallback的实现来看，是连接 SecurityCallback和KeyguardSecurityContainer的.代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">private</span> KeyguardSecurityCallback mCallback = <span class="keyword">new</span> KeyguardSecurityCallback() &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">userActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mSecurityCallback != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mSecurityCallback.userActivity();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">dismiss</span><span class="params">(<span class="keyword">boolean</span> authenticated, <span class="keyword">int</span> targetId)</span> </span>&#123;</span><br><span class="line">        mSecurityCallback.dismiss(authenticated, targetId);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isVerifyUnlockOnly</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mIsVerifyUnlockOnly;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reportUnlockAttempt</span><span class="params">(<span class="keyword">int</span> userId, <span class="keyword">boolean</span> success, <span class="keyword">int</span> timeoutMs)</span> </span>&#123;</span><br><span class="line">        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);</span><br><span class="line">        <span class="keyword">if</span> (success) &#123;</span><br><span class="line">            monitor.clearFailedUnlockAttempts();</span><br><span class="line">            mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            KeyguardSecurityContainer.<span class="keyword">this</span>.reportFailedUnlockAttempt(userId, timeoutMs);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mSecurityCallback.reset();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>KeyguardSecurityView:</strong> 接口， 定义了如下方法：<br>• setKeyguardCallback(KeyguardSecurityCallback callback);//Interface back to keyguard to tell it when security<br>• setLockPatternUtils(LockPatternUtils utils); //Set {@link LockPatternUtils} object. Useful for providing a mock interface.<br>• reset(); //Reset the view and prepare to take input. This should do things like clearing the password or pattern and clear error messages.<br>• onPause(); //Emulate activity life cycle within the view. When called, the view should clean up and prepare to be removed.<br>• onResume(int reason); //Emulate activity life cycle within this view.  When called, the view should prepare itself<br>• needsInput(); //Inquire whether this view requires IME (keyboard) interaction.<br>• KeyguardSecurityCallback getCallback(); //Get {@link KeyguardSecurityCallback} for the given object<br>• showPromptReason(int reason); //Show a string explaining why the security view needs to be solved.<br>• showMessage(String message, int color); //Show a message on the security view with a specified color<br>• showUsabilityHint(); //Instruct the view to show usability hints, if any.<br>• startAppearAnimation(); //Starts the animation which should run when the security view appears. //Security View显示动画<br>• startDisappearAnimation(Runnable finishRunnable); //Starts the animation which should run when the security view disappears.//Security View消失动画</p><p><strong>KeyguardSecurityModel:</strong> 定义了7种不同的锁屏模式(还有指纹解锁和面部解锁不在这里面)，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> SecurityMode &#123;</span><br><span class="line">        Invalid, <span class="comment">// NULL state</span></span><br><span class="line">        None, <span class="comment">// No security enabled</span></span><br><span class="line">        Pattern, <span class="comment">// Unlock by drawing a pattern.</span></span><br><span class="line">        Password, <span class="comment">// Unlock by entering an alphanumeric password</span></span><br><span class="line">        PIN, <span class="comment">// Strictly numeric password</span></span><br><span class="line">        SimPin, <span class="comment">// Unlock by entering a sim pin.</span></span><br><span class="line">        SimPuk <span class="comment">// Unlock by entering a sim puk</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>• 通过getSecurityMode方法获取当前的锁屏模式，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">SecurityMode <span class="title">getSecurityMode</span><span class="params">(<span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mIsPukScreenAvailable &amp;&amp; SubscriptionManager.isValidSubscriptionId(</span><br><span class="line">                monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) &#123;</span><br><span class="line">            <span class="keyword">return</span> SecurityMode.SimPuk;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (SubscriptionManager.isValidSubscriptionId(</span><br><span class="line">                monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) &#123;</span><br><span class="line">            <span class="keyword">return</span> SecurityMode.SimPin;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> security = mLockPatternUtils.getActivePasswordQuality(userId);</span><br><span class="line">        <span class="keyword">switch</span> (security) &#123;</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:</span><br><span class="line">                <span class="keyword">return</span> SecurityMode.PIN;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_MANAGED:</span><br><span class="line">                <span class="keyword">return</span> SecurityMode.Password;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:</span><br><span class="line">                <span class="keyword">return</span> SecurityMode.Pattern;</span><br><span class="line">            <span class="keyword">case</span> DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:</span><br><span class="line">                <span class="keyword">return</span> SecurityMode.None;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Unknown security quality:"</span> + security);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这里面有两个很重要的类LockPatternUtils和DevicePolicyManager，代码路径如下，具体分析见上面的模块分析.<br>frameworks/base/core/java/com/android/internal/widget/LockPatternUtils.java<br>frameworks/base/core/java/android/app/admin/DevicePolicyManager.java</p><p><strong>KeyguardSecurityViewFlipper:</strong> 继承自ViewFlipper，实现了KeyguardSecurityView接口.Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy. 用在keyguard_host_view布局中，嵌套与KeyguardSecurityContainer内部，具体见下面的keyguard_host_view.xml代码. 继承ViewFlipper的原因是，从滑动解锁界面上滑进入图案解锁、PIN码解锁、密码解锁等界面时有一个切换，实现视图翻转,另一方面，由于是KeyguardSecurityContainer的子视图，可以重写dispatchTouchEvent()函数，在各种解锁界面处理事件分发.<a href="">具体可以看ViewFlipper</a>.</p><p><strong>KeyguardSimPinView:</strong> 继承自KeyguardPinBasedInputView.用于显示SIM卡PIN码解锁界面逻辑的处理,实现了如下方法：<br>• KeyguardUpdateMonitorCallback中的onSimStateChanged来监听SIM卡的状态<br>• CheckSimPin 是个抽象类，继承自Thread，开启后台线程，用于验证PIN码正确性<br>• verifyPasswordAndUnlock 验证密码并解锁<br>• showDefaultMessage<br>• getSimRemainingAttemptsDialog<br>• getSimUnlockProgressDialog</p><p><strong>KeyguardSimPukView：</strong> 继承自KeyguardPinBasedInputView.用于显示SIM卡PUK码解锁界面逻辑的处理,实现了如下方法：<br>• KeyguardUpdateMonitorCallback中的onSimStateChanged来监听SIM卡的状态，与SIM卡的PIN码一样<br>• StateMachine 类，由于PUK码是在PIN码输入错三次都输入错误之后，才开启输入PUK码的，且PIN码要求是4-8位数字，PUK码必须是8位数字.此类用于此过程的检查<br>• CheckSimPuk  是个抽象类，继承自Thread，开启后台线程，用于验证PUK码正确性<br>• verifyPasswordAndUnlock 验证密码并解锁<br>• showDefaultMessage<br>• getSimRemainingAttemptsDialog<br>• getSimUnlockProgressDialog</p><p><strong>KeyguardStatusView：</strong> 继承自GridLayout，布局文件是keyguard_status_view.xml,主要用于处理Keyguard界面的时钟，是否在充电，以及用户信息,并包含了keyguard_status_area的布局.整体来说，用于处理显示在Keyguard界面StatusBar以下，Notification以上的界面布局的逻辑.</p><p><strong>KeyguardUpdateMonitor:</strong> 实现了TrustManager.TrustListener接口，注册了绝大多数的广播，负责处理界面的一些刷新流程处理.<br>到frameworks/base/core/java/android/app/trust/TrustManager.java里面看看TrustListener接口代码，如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TrustListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Reports that the trust state has changed.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> enabled if true, the system believes the environment to be trusted.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> userId the user, for which the trust changed.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> flags flags specified by the trust agent when granting trust. See</span></span><br><span class="line"><span class="comment">     *     &#123;<span class="doctag">@link</span> android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)</span></span><br><span class="line"><span class="comment">     *                 TrustAgentService.grantTrust(CharSequence, long, int)&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onTrustChanged</span><span class="params">(<span class="keyword">boolean</span> enabled, <span class="keyword">int</span> userId, <span class="keyword">int</span> flags)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Reports that whether trust is managed has changed</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> enabled if true, at least one trust agent is managing trust.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> userId the user, for which the state changed.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">onTrustManagedChanged</span><span class="params">(<span class="keyword">boolean</span> enabled, <span class="keyword">int</span> userId)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们来看一下，哪些广播会刷新：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_TIME_UPDATE = <span class="number">301</span>;  <span class="comment">//时间刷新</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_BATTERY_UPDATE = <span class="number">302</span>; <span class="comment">//电池状态刷新</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_SIM_STATE_CHANGE = <span class="number">304</span>; <span class="comment">//SIM卡状态改变</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_RINGER_MODE_CHANGED = <span class="number">305</span>;  <span class="comment">//响铃模式改变</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_PHONE_STATE_CHANGED = <span class="number">306</span>;  <span class="comment">//Phone状态改变</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_DEVICE_PROVISIONED = <span class="number">308</span>; <span class="comment">// 开机向导标记位</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_DPM_STATE_CHANGED = <span class="number">309</span>; <span class="comment">//DevicePolicyManage 设备授权</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_USER_SWITCHING = <span class="number">310</span>;  <span class="comment">// 切换用户</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_KEYGUARD_RESET = <span class="number">312</span>;  <span class="comment">//Keyguard重置</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_BOOT_COMPLETED = <span class="number">313</span>;  <span class="comment">//开机完成</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_USER_SWITCH_COMPLETE = <span class="number">314</span>; <span class="comment">//用户切换完成</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_USER_INFO_CHANGED = <span class="number">317</span>;  <span class="comment">// 用户信息变化</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_REPORT_EMERGENCY_CALL_ACTION = <span class="number">318</span>; <span class="comment">// 紧急拨号</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_STARTED_WAKING_UP = <span class="number">319</span>;  <span class="comment">// 开始唤醒</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_FINISHED_GOING_TO_SLEEP = <span class="number">320</span>; <span class="comment">//休眠完成</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_STARTED_GOING_TO_SLEEP = <span class="number">321</span>;  <span class="comment">//开始休眠</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_KEYGUARD_BOUNCER_CHANGED = <span class="number">322</span>; <span class="comment">//锁屏模式切换</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_FACE_UNLOCK_STATE_CHANGED = <span class="number">327</span>; <span class="comment">// 面部解锁状态改变</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_SIM_SUBSCRIPTION_INFO_CHANGED = <span class="number">328</span>; <span class="comment">//SIM卡SUBSCRIPTION信息变化</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_AIRPLANE_MODE_CHANGED = <span class="number">329</span>;  <span class="comment">//飞行模式切换</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_SERVICE_STATE_CHANGE = <span class="number">330</span>;   <span class="comment">// SIM卡服务状态切换</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_SCREEN_TURNED_ON = <span class="number">331</span>;   <span class="comment">//亮屏</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_SCREEN_TURNED_OFF = <span class="number">332</span>;  <span class="comment">//灭屏</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_DREAMING_STATE_CHANGED = <span class="number">333</span>;  <span class="comment">//屏保模式</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_USER_UNLOCKED = <span class="number">334</span>;    <span class="comment">//用户锁</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_LOCALE_CHANGED = <span class="number">500</span>;  <span class="comment">//语言切换</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_ASSISTANT_STACK_CHANGED = <span class="number">335</span>;  <span class="comment">// 指纹</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = <span class="number">336</span>; <span class="comment">//指纹认证</span></span><br></pre></td></tr></table></figure></li></ol><p> <strong>KeyguardUpdateMonitorCallback：</strong> 与KeyguardUpdateMonitor中广播消息对应的回调方法. Callback也是一种观察者模式，在Android的很多地方都有使用到.<br> <strong>现在下面要讲的是Android中经常用到的使用Callback来更新数据的一个实例，弄明白了，以后其他的使用也会很快理解到</strong></p><p> 在KeyguardUpdateMonitor中我们看看KeyguardUpdateMonitorCallback的使用.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;WeakReference&lt;KeyguardUpdateMonitorCallback&gt;&gt;</span><br><span class="line">        mCallbacks = Lists.newArrayList();</span><br></pre></td></tr></table></figure></p><p> 我们以MSG_TIME_UPDATE 时间的刷新为例，来看一下KeyguardUpdateMonitorCallback的用处.<br> 第一步：接收到时间刷新的广播<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> BroadcastReceiver mBroadcastReceiver = <span class="keyword">new</span> BroadcastReceiver() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceive</span><span class="params">(Context context, Intent intent)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">final</span> String action = intent.getAction();</span><br><span class="line">            <span class="keyword">if</span> (DEBUG) Log.d(TAG, <span class="string">"received broadcast "</span> + action);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (Intent.ACTION_TIME_TICK.equals(action)</span><br><span class="line">                    || Intent.ACTION_TIME_CHANGED.equals(action)</span><br><span class="line">                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) &#123;</span><br><span class="line">                mHandler.sendEmptyMessage(MSG_TIME_UPDATE);</span><br><span class="line">                ......</span><br></pre></td></tr></table></figure></p><p> 第二步：收到广播后，把消息发送给Handler，交由Handler来处理，我们来看看Handler收到MSG_TIME_UPDATE后的处理：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Handler mHandler = <span class="keyword">new</span> Handler() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">                <span class="keyword">case</span> MSG_TIME_UPDATE:</span><br><span class="line">                    handleTimeUpdate();</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> MSG_BATTERY_UPDATE:</span><br><span class="line">                    handleBatteryUpdate((BatteryStatus) msg.obj);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                    ......</span><br></pre></td></tr></table></figure></p><p> 第三步：Hanlder收到MSG_TIME_UPDATE消息后，执行handleTimeUpdate函数，我们来看看handleTimeUpdate函数代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Handle &#123;<span class="doctag">@link</span> #MSG_TIME_UPDATE&#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleTimeUpdate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG) Log.d(TAG, <span class="string">"handleTimeUpdate"</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; mCallbacks.size(); i++) &#123;</span><br><span class="line">        KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();</span><br><span class="line">        <span class="keyword">if</span> (cb != <span class="keyword">null</span>) &#123;</span><br><span class="line">            cb.onTimeChanged();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 第四步：在这里，我们看到了mCallbacks，从刚开始，我们看到mCallbacks的初始化，是空的，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;WeakReference&lt;KeyguardUpdateMonitorCallback&gt;&gt;</span><br><span class="line">            mCallbacks = Lists.newArrayList();</span><br></pre></td></tr></table></figure></p><p> 接着，在registerCallback函数里面进行了赋值，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerCallback</span><span class="params">(KeyguardUpdateMonitorCallback callback)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG) Log.v(TAG, <span class="string">"*** register callback for "</span> + callback);</span><br><span class="line">    <span class="comment">// Prevent adding duplicate callbacks</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; mCallbacks.size(); i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mCallbacks.get(i).get() == callback) &#123;</span><br><span class="line">            <span class="keyword">if</span> (DEBUG) Log.e(TAG, <span class="string">"Object tried to add another callback"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Exception(<span class="string">"Called by"</span>));</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    mCallbacks.add(<span class="keyword">new</span> WeakReference&lt;KeyguardUpdateMonitorCallback&gt;(callback));</span><br><span class="line">    removeCallback(<span class="keyword">null</span>); <span class="comment">// remove unused references</span></span><br><span class="line">    sendUpdates(callback);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 那么registerCallback又在什么时候调用呢？我们以EmergencyButton为例，看看EmergencyButton里面的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onAttachedToWindow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.onAttachedToWindow();</span><br><span class="line">    KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 我们看到在EmergencyButton类中的onAttachedToWindow方法使用registerCallback方法，也就是说只要EmergencyButton可见，那么就开始注册这个Callback回调.<br> 这里用EmergencyButton来举例好像不大合适，因为，在收到时间刷新的广播之后，第三步执行handleTimeUpdate方法，里面调用的Callback函数是cb.onTimeChanged();在EmergencyButton里面肯定是不需要关心时间的.</p><p> 我们找个与时间有关的，找到KeyguardStatusView，在这里面看到registerCallback的调用如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onAttachedToWindow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.onAttachedToWindow();</span><br><span class="line">    KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 和mergencyButton差不多，在KeyguardStatusView加载时候，就开始注册registerCallback方法.现在收到了时间更新的广播，执行到了第三步，现在要执行cb.onTimeChanged();我们看看KeyguardStatusView里面的实现：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> KeyguardUpdateMonitorCallback mInfoCallback = <span class="keyword">new</span> KeyguardUpdateMonitorCallback() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onTimeChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        refresh();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onKeyguardVisibilityChanged</span><span class="params">(<span class="keyword">boolean</span> showing)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (showing) &#123;</span><br><span class="line">            <span class="keyword">if</span> (DEBUG) Slog.v(TAG, <span class="string">"refresh statusview showing:"</span> + showing);</span><br><span class="line">            refresh();</span><br><span class="line">            updateOwnerInfo();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartedWakingUp</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        setEnableMarquee(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onFinishedGoingToSleep</span><span class="params">(<span class="keyword">int</span> why)</span> </span>&#123;</span><br><span class="line">        setEnableMarquee(<span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onUserSwitchComplete</span><span class="params">(<span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">        refresh();</span><br><span class="line">        updateOwnerInfo();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p> 从代码中可以看到，在KeyguardUpdateMonitor里面收到了时间刷新的广播之后，对应的KeyguardStatusView能立即执行refesh()方法，来刷新时间.<br> 从上面代码中还可以看到KeyguardStatusView在KeyguardUpdateMonitor收到唤醒，休眠以及用户切换等广播时，也能进行相应的更新。</p><p> <strong>LatencyTracker：</strong> 我们先来看看关于这个类的官方注释<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Class to track various latencies in SystemUI. It then outputs the latency to logcat so these latencies can be captured by tests and then used <span class="keyword">for</span> dashboards.</span><br><span class="line">This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but eventually we<span class="string">'d want to merge these two packages together so Keyguard can use common classes that are shared with SystemUI.</span></span><br></pre></td></tr></table></figure></p><p> 这个类主要的目的是用来跟踪SystemUI中各个小功能模块的耗时.方便后期性能分析和性能优化.这个类虽然在keyguard模块里面，由于Keyguard模块代码已经完全合入了SystemUI，所有他们两兄弟可以共用这个类.<br> 我们来看看跟踪了哪几个模块的耗时：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time it takes until the first frame of the notification panel to be displayed while expanding</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_EXPAND_PANEL = <span class="number">0</span>; <span class="comment">// 通知面板下拉时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time it takes until the first frame of recents is drawn after invoking it with the button.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_TOGGLE_RECENTS = <span class="number">1</span>; <span class="comment">//Recent 最近任务栏显示时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time between we get a fingerprint acquired signal until we start with the unlock animation</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_FINGERPRINT_WAKE_AND_UNLOCK = <span class="number">2</span>; <span class="comment">//指纹解锁时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time it takes to check PIN/Pattern/Password.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_CHECK_CREDENTIAL = <span class="number">3</span>;   <span class="comment">// PIN码解锁、图案解锁、密码解锁认证时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the</span></span><br><span class="line"><span class="comment"> * actions to unlock a user.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_CHECK_CREDENTIAL_UNLOCKED = <span class="number">4</span>; <span class="comment">//PIN码解锁、图案解锁、密码解锁从开始到解锁整个认证时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Time it takes to turn on the screen.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ACTION_TURN_ON_SCREEN = <span class="number">5</span>;  <span class="comment">//亮屏时间</span></span><br></pre></td></tr></table></figure></p><p> 上述跟踪的几个时间模块，对应SystemUI来说的确非常重要，这几个模块的响应时间太长的话，给用户的体验就不好，因为这几个是每个用户最经常使用的地方.<br> 从代码来看，这个功能开关是debug.systemui.latency_tracking这个prop属性<br> 接下来看看具体如何进行耗时跟踪的,主要是onActionStart和onActionEnd两个方法.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Notifies that an action is starting. This needs to be called from the main thread.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> action The action to start. One of the ACTION_* values.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onActionStart</span><span class="params">(<span class="keyword">int</span> action)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mEnabled) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], <span class="number">0</span>);</span><br><span class="line">    mStartRtc.put(action, SystemClock.elapsedRealtime());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Notifies that an action has ended. This needs to be called from the main thread.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> action The action to end. One of the ACTION_* values.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onActionEnd</span><span class="params">(<span class="keyword">int</span> action)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mEnabled) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">long</span> endRtc = SystemClock.elapsedRealtime();</span><br><span class="line">    <span class="keyword">long</span> startRtc = mStartRtc.get(action, -<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (startRtc == -<span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    mStartRtc.delete(action);</span><br><span class="line">    Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">long</span> duration = endRtc - startRtc;</span><br><span class="line">    Log.i(TAG, <span class="string">"action="</span> + action + <span class="string">" latency="</span> + duration);</span><br><span class="line">    EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, (<span class="keyword">int</span>) duration);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们以跟踪ACTION_TOGGLE_RECENTS,最近历史任务为例，在NavigationBarFragment.java的onRecentsClick方法中代码如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">onRecentsClick</span><span class="params">(View v)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (LatencyTracker.isEnabled(getContext())) &#123;</span><br><span class="line">        LatencyTracker.getInstance(getContext()).onActionStart(</span><br><span class="line">                LatencyTracker.ACTION_TOGGLE_RECENTS);</span><br><span class="line">    &#125;</span><br><span class="line">    mStatusBar.awakenDreams();</span><br><span class="line">    mCommandQueue.toggleRecentApps();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在RecentsActivity.java中，我们看到onActionEnd的调用如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> OnPreDrawListener mRecentsDrawnEventListener =</span><br><span class="line">        <span class="keyword">new</span> ViewTreeObserver.OnPreDrawListener() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onPreDraw</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                mRecentsView.getViewTreeObserver().removeOnPreDrawListener(<span class="keyword">this</span>);</span><br><span class="line">                EventBus.getDefault().post(<span class="keyword">new</span> RecentsDrawnEvent());</span><br><span class="line">                <span class="keyword">if</span> (LatencyTracker.isEnabled(getApplicationContext())) &#123;</span><br><span class="line">                    DejankUtils.postAfterTraversal(() -&gt; LatencyTracker.getInstance(</span><br><span class="line">                            getApplicationContext()).onActionEnd(</span><br><span class="line">                            LatencyTracker.ACTION_TOGGLE_RECENTS));</span><br><span class="line">                &#125;</span><br><span class="line">                DejankUtils.postAfterTraversal(() -&gt; &#123;</span><br><span class="line">                    Recents.getTaskLoader().startLoader(RecentsActivity.<span class="keyword">this</span>);</span><br><span class="line">                    Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(<span class="keyword">true</span>);</span><br><span class="line">                &#125;);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br></pre></td></tr></table></figure></p><p> 耗时计算的核心是onActionStart和onActionEnd两个方法的实现，需要追踪Trace.asyncTraceBegin和Trace.asyncTraceEnd两个方法，这里先不讲，偏题太远了.</p><p> <strong>LiftToActivateListener:</strong> 继承自View.OnHoverListener，我们来看看官方的注释：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hover listener that implements lift-to-activate interaction <span class="keyword">for</span> accessibility. May be added to multiple views.</span><br></pre></td></tr></table></figure></p><p> 通过继承View.OnHoverListener实现悬停监听，View.OnHoverListener是Android 4.0增加的对光标悬停事件、手写笔、鼠标按钮事件的支持。<br> 主要用于ACCESSIBILITY_SERVICE，辅助服务</p><p><strong>NumPadKey：</strong> 主要用于Keyguard模块中数字数码</p><p><strong>PasswordTextView：</strong> 与TextView相似，但是属于密码输入，且有动画</p><p><strong>SecurityMessageDisplay:</strong> 接口，定义了Keyguard中显示信息的几个方法.</p><p><strong>ViewMediatorCallback:</strong> 接口，The callback used by the keyguard view to tell the {@link KeyguardViewMediator} various things.这个Callback 主要用于 KeyguardViewMediator中 更新Keyguard的View，和上面讲到的三个Callback实现起来不一样. ViewMediatorCallback接口是在KeyguardViewMediator中通过内部类来实现，如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><span class="line">ViewMediatorCallback mViewMediatorCallback = <span class="keyword">new</span> ViewMediatorCallback() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">userActivity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        KeyguardViewMediator.<span class="keyword">this</span>.userActivity();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">keyguardDone</span><span class="params">(<span class="keyword">boolean</span> strongAuth, <span class="keyword">int</span> targetUserId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetUserId != ActivityManager.getCurrentUser()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        tryKeyguardDone();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">keyguardDoneDrawing</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Trace.beginSection(<span class="string">"KeyguardViewMediator.mViewMediatorCallback#keyguardDoneDrawing"</span>);</span><br><span class="line">        mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);</span><br><span class="line">        Trace.endSection();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setNeedsInput</span><span class="params">(<span class="keyword">boolean</span> needsInput)</span> </span>&#123;</span><br><span class="line">        mStatusBarKeyguardViewManager.setNeedsInput(needsInput);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">keyguardDonePending</span><span class="params">(<span class="keyword">boolean</span> strongAuth, <span class="keyword">int</span> targetUserId)</span> </span>&#123;</span><br><span class="line">        Trace.beginSection(<span class="string">"KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending"</span>);</span><br><span class="line">        <span class="keyword">if</span> (targetUserId != ActivityManager.getCurrentUser()) &#123;</span><br><span class="line">            Trace.endSection();</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        mKeyguardDonePending = <span class="keyword">true</span>;</span><br><span class="line">        mHideAnimationRun = <span class="keyword">true</span>;</span><br><span class="line">        mHideAnimationRunning = <span class="keyword">true</span>;</span><br><span class="line">        mStatusBarKeyguardViewManager.startPreHideAnimation(mHideAnimationFinishedRunnable);</span><br><span class="line">        mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,</span><br><span class="line">                KEYGUARD_DONE_PENDING_TIMEOUT_MS);</span><br><span class="line">        Trace.endSection();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">keyguardGone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Trace.beginSection(<span class="string">"KeyguardViewMediator.mViewMediatorCallback#keyguardGone"</span>);</span><br><span class="line">        mKeyguardDisplayManager.hide();</span><br><span class="line">        Trace.endSection();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">readyForKeyguardDone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Trace.beginSection(<span class="string">"KeyguardViewMediator.mViewMediatorCallback#readyForKeyguardDone"</span>);</span><br><span class="line">        <span class="keyword">if</span> (mKeyguardDonePending) &#123;</span><br><span class="line">            mKeyguardDonePending = <span class="keyword">false</span>;</span><br><span class="line">            tryKeyguardDone();</span><br><span class="line">        &#125;</span><br><span class="line">        Trace.endSection();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resetKeyguard</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        resetStateLocked();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">playTrustedSound</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        KeyguardViewMediator.<span class="keyword">this</span>.playTrustedSound();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isScreenOn</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mDeviceInteractive;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getBouncerPromptReason</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> currentUser = ActivityManager.getCurrentUser();</span><br><span class="line">        <span class="keyword">boolean</span> trust = mTrustManager.isTrustUsuallyManaged(currentUser);</span><br><span class="line">        <span class="keyword">boolean</span> fingerprint = mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser);</span><br><span class="line">        <span class="keyword">boolean</span> any = trust || fingerprint;</span><br><span class="line">        KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =</span><br><span class="line">                mUpdateMonitor.getStrongAuthTracker();</span><br><span class="line">        <span class="keyword">int</span> strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (any &amp;&amp; !strongAuthTracker.hasUserAuthenticatedSinceBoot()) &#123;</span><br><span class="line">            <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_RESTART;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (any &amp;&amp; (strongAuth &amp; STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_TIMEOUT;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (any &amp;&amp; (strongAuth &amp; STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (trust &amp;&amp; (strongAuth &amp; SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (any &amp;&amp; (strongAuth &amp; STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> KeyguardSecurityView.PROMPT_REASON_NONE;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onSecondaryDisplayShowingChanged</span><span class="params">(<span class="keyword">int</span> displayId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (KeyguardViewMediator.<span class="keyword">this</span>) &#123;</span><br><span class="line">            setShowingLocked(mShowing, displayId, <span class="keyword">false</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p> 对于ViewMediatorCallback接口函数的作用，我们可以看到在KeyguardViewMediator的setupLocked()函数中，将ViewMediatorCallback作为一个参数传入了KeyguardDisplayManager类里面.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">setupLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);</span><br><span class="line">        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);</span><br><span class="line"></span><br><span class="line">        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, <span class="string">"show keyguard"</span>);</span><br><span class="line">        mShowKeyguardWakeLock.setReferenceCounted(<span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">        IntentFilter filter = <span class="keyword">new</span> IntentFilter();</span><br><span class="line">        filter.addAction(DELAYED_KEYGUARD_ACTION);</span><br><span class="line">        filter.addAction(DELAYED_LOCK_PROFILE_ACTION);</span><br><span class="line">        filter.addAction(Intent.ACTION_SHUTDOWN);</span><br><span class="line">        mContext.registerReceiver(mBroadcastReceiver, filter);</span><br><span class="line"></span><br><span class="line">        mKeyguardDisplayManager = <span class="keyword">new</span> KeyguardDisplayManager(mContext, mViewMediatorCallback);</span><br><span class="line">        ...</span><br></pre></td></tr></table></figure></p><p> 那么再到KeyguardDisplayManager里面看看ViewMediatorCallback的使用，如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">KeyguardDisplayManager</span><span class="params">(Context context, ViewMediatorCallback callback)</span> </span>&#123;</span><br><span class="line">    mContext = context;</span><br><span class="line">    mCallback = callback;</span><br><span class="line">    mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);</span><br><span class="line">&#125;</span><br><span class="line">....</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">updateDisplays</span><span class="params">(<span class="keyword">boolean</span> showing)</span> </span>&#123;</span><br><span class="line">    Presentation originalPresentation = mPresentation;</span><br><span class="line">    ...</span><br><span class="line">     <span class="comment">// mPresentation is only updated when the display changes</span></span><br><span class="line">    <span class="keyword">if</span> (mPresentation != originalPresentation) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> displayId = mPresentation != <span class="keyword">null</span></span><br><span class="line">                ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;</span><br><span class="line">        mCallback.onSecondaryDisplayShowingChanged(displayId);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 从这里可以看到，KeyguardDisplayManager里面通过ViewMediatorCallback接口的onSecondaryDisplayShowingChanged,反过来通知KeyguardViewMediator做出相应的动作.<br> 从之前的KeyguardViewMediator代码分析可知整个待机解/锁屏业务的调度器，负责调度锁屏界面的相关动作及查询解锁屏状态的一个服务.</p><p> 至此，Keyguard模块的纯业务逻辑已经分析完毕.</p><h2 id="SystemUI中Keyguard的UI显示模块代码"><a href="#SystemUI中Keyguard的UI显示模块代码" class="headerlink" title="SystemUI中Keyguard的UI显示模块代码"></a>SystemUI中Keyguard的UI显示模块代码</h2><ol start="5"><li><p>SystemUI中的Keyguard模块中UI显示相关的代码有：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_message_area_large.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml</span><br><span class="line">frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml</span><br></pre></td></tr></table></figure><p><strong>keyguard_bouncer:</strong>是一个FrameLayout，include了keyguard_host_view文件.</p><p><strong>keyguard_emergency_carrier_area:</strong>锁屏上紧急拨号的UI处理，包含了显示当前运营商信息的CarrierText和一个接入紧急拨号界面的EmergencyButton.</p><p><strong>keyguard_host_view:</strong>This is the host view that generally contains two sub views: the widget view and the security view.<br>这是Keyguard显示模块主要的布局文件，包含两个子布局：一个带小部件(时钟)的布局，还有一个是安全布局(指的是图案解锁/密码/PIN码解锁界面)<br>功能由KeyguardHostView.java KeyguardSecurityContainer.java KeyguardSecurityViewFlipper.java实现<br>具体代码如下：</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">&lt;com.android.keyguard.KeyguardHostView</span><br><span class="line">    xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    xmlns:androidprv=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span><br><span class="line">    android:id=<span class="string">"@+id/keyguard_host_view"</span></span><br><span class="line">    android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">    android:clipChildren=<span class="string">"false"</span></span><br><span class="line">    android:clipToPadding=<span class="string">"false"</span></span><br><span class="line">    android:importantForAccessibility=<span class="string">"yes"</span>&gt; &lt;!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent</span><br><span class="line">                                                  from <span class="keyword">this</span> view when bouncer is shown --&gt;</span><br><span class="line"></span><br><span class="line">    &lt;com.android.keyguard.KeyguardSecurityContainer</span><br><span class="line">        android:id=<span class="string">"@+id/keyguard_security_container"</span></span><br><span class="line">        android:layout_width=<span class="string">"wrap_content"</span></span><br><span class="line">        android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line">        androidprv:layout_maxHeight=<span class="string">"@dimen/keyguard_security_max_height"</span></span><br><span class="line">        android:clipChildren=<span class="string">"false"</span></span><br><span class="line">        android:clipToPadding=<span class="string">"false"</span></span><br><span class="line">        android:padding=<span class="string">"0dp"</span></span><br><span class="line">        android:layout_gravity=<span class="string">"center"</span>&gt;</span><br><span class="line">        &lt;com.android.keyguard.KeyguardSecurityViewFlipper</span><br><span class="line">            android:id=<span class="string">"@+id/view_flipper"</span></span><br><span class="line">            android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">            android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">            android:clipChildren=<span class="string">"false"</span></span><br><span class="line">            android:clipToPadding=<span class="string">"false"</span></span><br><span class="line">            android:paddingTop=<span class="string">"@dimen/keyguard_security_view_top_margin"</span></span><br><span class="line">            android:paddingStart=<span class="string">"@dimen/keyguard_security_view_lateral_margin"</span></span><br><span class="line">            android:paddingEnd=<span class="string">"@dimen/keyguard_security_view_lateral_margin"</span></span><br><span class="line">            android:gravity=<span class="string">"center"</span>&gt;</span><br><span class="line">        &lt;/com.android.keyguard.KeyguardSecurityViewFlipper&gt;</span><br><span class="line">    &lt;/com.android.keyguard.KeyguardSecurityContainer&gt;</span><br><span class="line"></span><br><span class="line">&lt;/com.android.keyguard.KeyguardHostView&gt;</span><br></pre></td></tr></table></figure><p><strong>keyguard_esim_area:</strong>This contains disable esim buttonas shared by sim_pin/sim_puk screens<br>SIM卡的PIN码和PUK码解锁时，禁用esim卡的按钮，功能由KeyguardEsimArea.java来实现.</p><p><strong>keyguard_message_area_large:</strong>This contains emergency call button and carrier as shared by pin/pattern/password screens.<br>PIN码/图案/密码解锁界面时显示输入密码错误，以及输入次数等消息,由KeyguardMessageArea.java来实现</p><p><strong>keyguard_message_area：</strong> 同上面的keyguard_message_area_large</p><p><strong>keyguard_num_pad_key:</strong></p><p><strong>keyguard_password_view:</strong> 密码输入界面</p><p><strong>keyguard_pattern_view：</strong> 图案解锁界面</p><p><strong>keyguard_pin_view：</strong> PIN码解锁界面</p><p><strong>keyguard_presentation：</strong> This is a view that shows general status information in Keyguard.</p><p><strong>keyguard_sim_pin_view:</strong> SIM卡PIN码解锁界面</p><p><strong>keyguard_sim_puk_view：</strong> SIM卡PUK码解锁界面</p><p><strong>keyguard_status_area：</strong> 锁屏界面的日期和闹钟</p><p><strong>keyguard_status_view：</strong> This is a view that shows general status information in<br>Keyguard.由KeyguardStatusView实现，主要显示Keyguard界面的时钟，是否在充电，以及用户信息,并包含了keyguard_status_area的布局.整体来说，用于显示Keyguard界面StatusBar以下，Notification以上的界面布局.</p></li></ol><h1 id="Keyguard里的一些属性配置"><a href="#Keyguard里的一些属性配置" class="headerlink" title="Keyguard里的一些属性配置"></a>Keyguard里的一些属性配置</h1><p> <strong>config_voice_capable</strong><br> <strong>config_enable_emergency_call_while_sim_locked</strong> SIM卡锁的时候，是否能紧急拨号的开关<br> <strong>debug.systemui.latency_tracking</strong> true:且debug模式开启模块耗时跟踪 false：关闭模块耗时跟踪<br> <strong>velocity_tracker_impl</strong> 配置滑动速度跟踪是使用platform 还是 noisy</p><h1 id="锁屏界面的布局"><a href="#锁屏界面的布局" class="headerlink" title="锁屏界面的布局"></a>锁屏界面的布局</h1><h1 id="解锁界面的显示与隐藏"><a href="#解锁界面的显示与隐藏" class="headerlink" title="解锁界面的显示与隐藏"></a>解锁界面的显示与隐藏</h1><h1 id="滑动解锁界面事件处理"><a href="#滑动解锁界面事件处理" class="headerlink" title="滑动解锁界面事件处理"></a>滑动解锁界面事件处理</h1><p> 为了理清楚滑动解锁的时候，滑动事件传递以及处理逻辑的流程，首先需要理清楚滑动解锁界面View的层级关系.<br> 通过使用DDMS中的Hierarchy工具，查看滑动解锁界面布局，发现顶层布局是id为notification_panel,布局文件为res/layout/status_bar_expanded.xml 如下：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">&lt;com.android.systemui.statusbar.phone.NotificationPanelView</span><br><span class="line">    xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    xmlns:systemui=<span class="string">"http://schemas.android.com/apk/res-auto"</span></span><br><span class="line">    android:id=<span class="string">"@+id/notification_panel"</span></span><br><span class="line">    android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">    android:background=<span class="string">"@android:color/transparent"</span> &gt;</span><br><span class="line"></span><br><span class="line">    &lt;include</span><br><span class="line">        layout=<span class="string">"@layout/keyguard_status_view"</span></span><br><span class="line">        android:visibility=<span class="string">"gone"</span> /&gt; <span class="comment">//锁屏界面中时钟充电图标等显示</span></span><br><span class="line"></span><br><span class="line">    &lt;com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer</span><br><span class="line">        ....</span><br><span class="line">        &lt;include</span><br><span class="line">            layout=<span class="string">"@layout/keyguard_status_bar"</span></span><br><span class="line">            android:visibility=<span class="string">"invisible"</span> /&gt;</span><br><span class="line">            .....</span><br><span class="line">    &lt;include</span><br><span class="line">        layout=<span class="string">"@layout/keyguard_bottom_area"</span></span><br><span class="line">        android:visibility=<span class="string">"gone"</span> /&gt; <span class="comment">//锁屏界面底部UI显示</span></span><br><span class="line"></span><br><span class="line">&lt;/com.android.systemui.statusbar.phone.NotificationPanelView&gt;</span><br></pre></td></tr></table></figure></p><p> 从status_bar_expanded.xml的布局可以看到，正常情况下keyguard_status_view和keyguard_bottom_area以及keyguard_status_bar三个部分默认都是gone或者invisible，不显示，也就是说非Keyguard界面时，这两个显示Keyguard的头部信息和底部信息的布局是不显示.<br> 在布局上，将Keyguard的头部和底部显示放到了通知面板NotificationPanelView中，则锁屏和非锁屏界面通知面板的滑动处理在NotificationPanelView中进行.<br> 我们来看看 NotificationPanelView的继承关系：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NotificationPanelView</span> <span class="keyword">extends</span> <span class="title">PanelView</span> <span class="keyword">implements</span></span></span><br><span class="line"><span class="class">        <span class="title">ExpandableView</span>.<span class="title">OnHeightChangedListener</span>,</span></span><br><span class="line"><span class="class">        <span class="title">View</span>.<span class="title">OnClickListener</span>, <span class="title">NotificationStackScrollLayout</span>.<span class="title">OnOverscrollTopChangedListener</span>,</span></span><br><span class="line"><span class="class">        <span class="title">KeyguardAffordanceHelper</span>.<span class="title">Callback</span>, <span class="title">NotificationStackScrollLayout</span>.<span class="title">OnEmptySpaceClickListener</span>,</span></span><br><span class="line"><span class="class">        <span class="title">OnHeadsUpChangedListener</span>, <span class="title">QS</span>.<span class="title">HeightListener</span> </span>&#123;</span><br><span class="line">        ...</span><br></pre></td></tr></table></figure></p><p> NotificationPanelView继承自PanelView：  PanelView是一个抽象类，且其为抽象类PanelBar的内部类，PanelBar由PhoneStatusBarView实现.<br> 实现了：<br> • ExpandableView.OnHeightChangedListener<br> • View.OnClickListener<br> • NotificationStackScrollLayout.OnOverscrollTopChangedListener<br> • KeyguardAffordanceHelper.Callback  //Keyguard底部进入Phone和Camera的入口<br> • NotificationStackScrollLayout.OnEmptySpaceClickListener<br> • OnHeadsUpChangedListener<br> • QS.HeightListener</p><p> NotificationPanelView中与滑动事件有关的函数：<br> • onInterceptTouchEvent<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onInterceptTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mBlockTouches || mQs.isCustomizing()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    initDownStates(event);</span><br><span class="line">    <span class="keyword">if</span> (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) &#123;</span><br><span class="line">        mIsExpansionFromHeadsUp = <span class="keyword">true</span>;</span><br><span class="line">        MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, <span class="number">1</span>);</span><br><span class="line">        MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!isFullyCollapsed() &amp;&amp; onQsIntercept(event)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.onInterceptTouchEvent(event);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •<br> •</p><p> MetricsLogger: frameworks/base/core/java/com/android/internal/logging/MetricsLogger.java<br> LockscreenGestureLogger.java </p><p> DozeLog.traceFling</p><p> 解锁流程：</p><p> 我们来看看实现解锁的方式有哪些：</p><p> • (1)当Keyguard被另外的窗口遮挡是，可以调用KeyguardService.java里的setOccluded方法，达到Hide Keyguard的目的，此时也可以实现解锁.<br> KeyguardViewMediator.java setOccluded() -&gt; 收到 SET_OCCLUDED 消息-&gt; handleSetOccluded() -&gt; startKeyguardExitAnimation() 发送 START_KEYGUARD_EXIT_ANIM 消息</p><p> • (2)当用户手动按Power键灭屏或者灭屏时间到了自动灭屏，都可以调用KeyguardService.java里的onStartedGoingToSleep(int reason)方法来实现解锁<br>  KeyguardViewMediator.java onStartedGoingToSleep() -&gt; hideLocked() 发送HIDE 消息</p><p> • (3)对于APP侧可以调用KeyguardService.java里的setKeyguardEnabled方法来实现上锁和解锁，常用的情况是：锁屏时来电. KeyguardViewMediator.java setKeyguardEnabled() -&gt; hideLocked() 发送HIDE 消息</p><p> • (4)KeyguardViewMediator.java doKeyguardLocked() -&gt; hideLocked() 发送HIDE 消息</p><p>  isGoingToNotificationShade  mKeyguardGoingAwayRunnable 也是隐藏Keyguard的线程</p><p> KeyguardViewMediator.java 接到消息 HIDE 或者 START_KEYGUARD_EXIT_ANIM 则-&gt; handleHide() -&gt; handleStartKeyguardExitAnimation()<br> StatusBarKeyguardViewManager.java -&gt; hide()<br> StatusBar.java -&gt; hideKeyguard() -&gt; updateIsKeyguard() -&gt; hideKeyguardImpl() -&gt; updateKeyguardState()<br> NotificationPanelView.java -&gt; setBarState()</p><p> readyForKeyguardDone</p><p> 解锁方式那么多，那么是如何实现滑动解锁的呢？</p><p> 区别： onQsHeightChanged 执行了，解开锁</p><p>向上滑动–&gt; 解锁<br>向下滑动–&gt; 下拉通知栏</p><p> 从事件分发角度来看滑动解锁事件的分发：<br>                        onInterceptTouchEvent     dispatchTouchEvent         onTouchEvent<br> PanelView                                           无<br> NotificationPanelView                               无</p><p> Down事件开始<br> 第一步： NotificationPanelView的 onInterceptTouchEvent<br> return ture,则交给自己的onTouchEvent方法处理<br> return false,则交给PanelView的onInterceptTouchEvent方法处理<br> 此步：return false</p><p> 第二步：从log来看，第一步return false，则交给PanelView的onInterceptTouchEvent方法处理<br> return ture,则交给自己的onTouchEvent方法处理<br> return false,则交给子类的onTouchEvent方法处理<br> 此步：return false</p><p> 第三步：交给NotificationPanelViewt 的 onTouchEvent处理</p><p> 第四步：交给PanelView 的 onTouchEvent处理</p><p> 第五步：交给StatusBarWindowView的onInterceptTouchEvent 处理</p><p> Move事件开始<br> 第六步: 交给NotificationPanelView 的 onTouchEvent</p><p> 第七步：交给PanelView 的 onTouchEvent</p><p> 滑动解锁，滑不开可能的原因：<br> 1) 滑动距离不够，此时需要修改滑动解锁距离的阈值,通过修改xml文件中unlock_falsing_threshold的值即可，Android默认是80dp.<br> 或者修改PanelView.java里面的loadDimens函数的mUnlockFalsingThreshold值.<br> 查看是否是由于滑动距离不够导致无法解锁的方法：<br> 在PanelView.java的onTouchEvent函数中打印-h的值，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">case</span> MotionEvent.ACTION_MOVE:</span><br><span class="line">            log(<span class="string">"onTouchEvent,ACTION_MOVE"</span>);</span><br><span class="line">            trackMovement(event);</span><br><span class="line">            <span class="keyword">float</span> h = y - mInitialTouchY;</span><br><span class="line">            ...</span><br><span class="line">            <span class="keyword">if</span> (-h &gt;= getFalsingThreshold()) &#123;</span><br><span class="line">                mTouchAboveFalsingThreshold = <span class="keyword">true</span>;</span><br><span class="line">                mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);</span><br><span class="line">            &#125;</span><br><span class="line">            Log.d(TAG+<span class="string">"eric"</span>, <span class="string">"-h="</span>+(-h)+<span class="string">" getFalsingThreshold()="</span>+getFalsingThreshold()+<span class="string">",mUpwardsWhenTresholdReached="</span>+mUpwardsWhenTresholdReached);</span><br></pre></td></tr></table></figure></p><p> 打印上述log，假如当前mUnlockFalsingThreshold的值为80，我们收集解锁失败的log，如果从log中看到我们的-h的值都是小于80的话，可以通过将mUnlockFalsingThreshold的值改小的方式解决，但是如果，此时-h的值都大于等于mUnlockFalsingThreshold了，还是解锁失败呢？那就要看第二种情况了.</p><p> 2)滑动距离满足条件，但是还是很大概率的解锁失败.<br> 此时可以通过关闭xml中的config_lockscreenAntiFalsingClassifierEnabled值的方式来解决.具体看HumanInteractionClassifier.java中<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">updateConfiguration</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> defaultValue = mContext.getResources().getBoolean(</span><br><span class="line">            R.bool.config_lockscreenAntiFalsingClassifierEnabled);</span><br><span class="line"></span><br><span class="line">    mEnableClassifier = <span class="number">0</span> != Settings.Global.getInt(</span><br><span class="line">            mContext.getContentResolver(),</span><br><span class="line">            HIC_ENABLE, defaultValue ? <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isFalseTouch</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mEnableClassifier) &#123;</span><br><span class="line">        <span class="keyword">float</span> evaluation = mHistoryEvaluator.getEvaluation();</span><br><span class="line">        <span class="keyword">boolean</span> result = evaluation &gt;= <span class="number">5.0f</span>;<span class="comment">//这个5.0f的值具体是怎么定义的？</span></span><br><span class="line">        <span class="keyword">if</span> (FalsingLog.ENABLED) &#123;</span><br><span class="line">            FalsingLog.i(<span class="string">"isFalseTouch"</span>, <span class="keyword">new</span> StringBuilder()</span><br><span class="line">                    .append(<span class="string">"eval="</span>).append(evaluation).append(<span class="string">" result="</span>)</span><br><span class="line">                    .append(result ? <span class="number">1</span> : <span class="number">0</span>).toString());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 通过设置config_lockscreenAntiFalsingClassifierEnabled为false或者，我们可以通过将isFalseTouch函数中的boolean result = evaluation &gt;= 5.0f;中的5.0f调高到6.0f.</p><p> 在KeguardDisplayManager的hide()函数里面打印调用堆栈如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    : java.lang.RuntimeException: here</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.keyguard.KeyguardDisplayManager.hide(KeyguardDisplayManager.java:<span class="number">64</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.systemui.keyguard.KeyguardViewMediator$<span class="number">2</span>.keyguardGone(KeyguardViewMediator.java:<span class="number">601</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.hide(StatusBarKeyguardViewManager.java:<span class="number">440</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.systemui.keyguard.KeyguardViewMediator.handleStartKeyguardExitAnimation(KeyguardViewMediator.java:<span class="number">1847</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.systemui.keyguard.KeyguardViewMediator.-wrap16(Unknown Source:<span class="number">0</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.systemui.keyguard.KeyguardViewMediator$<span class="number">4</span>.handleMessage(KeyguardViewMediator.java:<span class="number">1561</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at android.os.Handler.dispatchMessage(Handler.java:<span class="number">106</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at android.os.Looper.loop(Looper.java:<span class="number">164</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at android.app.ActivityThread.main(ActivityThread.java:<span class="number">6501</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at java.lang.reflect.Method.invoke(Native Method)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:<span class="number">438</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.971</span>  <span class="number">9378</span>  <span class="number">9378</span> W eric    :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">807</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">19</span>:<span class="number">13</span>:<span class="number">50.972</span>  <span class="number">9378</span>  <span class="number">9378</span> V KeyguardDisplayManager: hide</span><br></pre></td></tr></table></figure></p><p> 从上面的堆栈调用，可以看到hide()方法，是在收到START_KEYGUARD_EXIT_ANIM这个Message之后触发,START_KEYGUARD_EXIT_ANIM消息是在startKeyguardExitAnimation函数里面发出来的，继续查看startKeyguardExitAnimation函数的调用堆栈.<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    : Called: </span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    : java.lang.RuntimeException: startKeyguardExitAnimation</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    :  at com.android.systemui.keyguard.KeyguardViewMediator.startKeyguardExitAnimation(KeyguardViewMediator.java:<span class="number">2020</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    :  at com.android.systemui.keyguard.KeyguardService$<span class="number">1</span>.startKeyguardExitAnimation(KeyguardService.java:<span class="number">222</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    :  at com.android.internal.policy.IKeyguardService$Stub.onTransact(IKeyguardService.java:<span class="number">203</span>)</span><br><span class="line"><span class="number">01</span>-<span class="number">03</span> <span class="number">21</span>:<span class="number">39</span>:<span class="number">58.318</span> <span class="number">10956</span> <span class="number">11005</span> W eric    :  at android.os.Binder.execTransact(Binder.java:<span class="number">697</span>)</span><br></pre></td></tr></table></figure></p><p> startKeyguardExitAnimation 在 handleStartTransitionForKeyguardLw</p><p> 在前面的KeyguardManager代码分析里面讲到了KeyguardLock内部类，虽然已经过时，被弃用，接着往下看如何实现显示和隐藏锁屏界面的，我们从代码看到，会调用到WindowManagerService.java里面的disableKeyguard和reenableKeyguard方法.<br>  继续跟到WindowManagerService.java里面看disableKeyguard的实现：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">disableKeyguard</span><span class="params">(IBinder token, String tag)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)</span><br><span class="line">        != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(<span class="string">"Requires DISABLE_KEYGUARD permission"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// If this isn't coming from the system then don't allow disabling the lockscreen</span></span><br><span class="line">    <span class="comment">// to bypass security.</span></span><br><span class="line">    <span class="keyword">if</span> (Binder.getCallingUid() != SYSTEM_UID &amp;&amp; isKeyguardSecure()) &#123;</span><br><span class="line">        Log.d(TAG_WM, <span class="string">"current mode is SecurityMode, ignore disableKeyguard"</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If this isn't coming from the current profiles, ignore it.</span></span><br><span class="line">    <span class="keyword">if</span> (!isCurrentProfileLocked(UserHandle.getCallingUserId())) &#123;</span><br><span class="line">        Log.d(TAG_WM, <span class="string">"non-current profiles, ignore disableKeyguard"</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (token == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"token == null"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(</span><br><span class="line">            KeyguardDisableHandler.KEYGUARD_DISABLE, <span class="keyword">new</span> Pair&lt;IBinder, String&gt;(token, tag)));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 可以看到通过KeyguardDisableHandler，发送消息KEYGUARD_DISABLE，继续查阅KeyguardDisableHandler代码看到开始匹配密码.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mKeyguardTokenWatcher == <span class="keyword">null</span>) &#123;</span><br><span class="line">        mKeyguardTokenWatcher = <span class="keyword">new</span> KeyguardTokenWatcher(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">        <span class="keyword">case</span> KEYGUARD_DISABLE:</span><br><span class="line">            <span class="keyword">final</span> Pair&lt;IBinder, String&gt; pair = (Pair&lt;IBinder, String&gt;)msg.obj;</span><br><span class="line">            mKeyguardTokenWatcher.acquire(pair.first, pair.second);</span><br><span class="line">            <span class="keyword">break</span>;</span><br></pre></td></tr></table></figure></p><h1 id="滑动实现解锁"><a href="#滑动实现解锁" class="headerlink" title="滑动实现解锁"></a>滑动实现解锁</h1><p> 滑动解锁涉及到的代码有：<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java<br> PanelView是一个抽象类，继承自FrameLayout<br> NotificationPanelView 继承自 PanelView</p><p> KeyguardAffordanceHelper.java 锁屏界面滑动Phone和Camera图标，分别启动Phone和Camera的事件处理<br> A touch handler of the keyguard which is responsible for launching phone and camera affordances.<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java </p><p> PanelView.java中相关函数解析</p><p> onTrackingStarted</p><p> trackMovement()</p><p> onTouchEvent函数<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mInstantExpanding || mTouchDisabled</span><br><span class="line">            || (mMotionAborted &amp;&amp; event.getActionMasked() != MotionEvent.ACTION_DOWN)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If dragging should not expand the notifications shade, then return false.</span></span><br><span class="line">    <span class="keyword">if</span> (!mNotificationsDragEnabled) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mTracking) &#123;</span><br><span class="line">            <span class="comment">// Turn off tracking if it's on or the shade can get stuck in the down position.</span></span><br><span class="line">            onTrackingStopped(<span class="keyword">true</span> <span class="comment">/* expand */</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// On expanding, single mouse click expands the panel instead of dragging.</span></span><br><span class="line">    <span class="keyword">if</span> (isFullyCollapsed() &amp;&amp; event.isFromSource(InputDevice.SOURCE_MOUSE)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (event.getAction() == MotionEvent.ACTION_UP) &#123;</span><br><span class="line">            expand(<span class="keyword">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * We capture touch events here and update the expand height here in case according to</span></span><br><span class="line"><span class="comment">     * the users fingers. This also handles multi-touch.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * If the user just clicks shortly, we show a quick peek of the shade.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Flinging is also enabled in order to open or close the shade.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> pointerIndex = event.findPointerIndex(mTrackingPointer);</span><br><span class="line">    <span class="keyword">if</span> (pointerIndex &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        pointerIndex = <span class="number">0</span>;</span><br><span class="line">        mTrackingPointer = event.getPointerId(pointerIndex);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">float</span> x = event.getX(pointerIndex);</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">float</span> y = event.getY(pointerIndex);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (event.getActionMasked() == MotionEvent.ACTION_DOWN) &#123;</span><br><span class="line">        mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();</span><br><span class="line">        mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (event.getActionMasked()) &#123;</span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_DOWN:</span><br><span class="line">            startExpandMotion(x, y, <span class="keyword">false</span> <span class="comment">/* startTracking */</span>, mExpandedHeight);</span><br><span class="line">            mJustPeeked = <span class="keyword">false</span>;</span><br><span class="line">            mMinExpandHeight = <span class="number">0.0f</span>;</span><br><span class="line">            mPanelClosedOnDown = isFullyCollapsed();</span><br><span class="line">            mHasLayoutedSinceDown = <span class="keyword">false</span>;</span><br><span class="line">            mUpdateFlingOnLayout = <span class="keyword">false</span>;</span><br><span class="line">            mMotionAborted = <span class="keyword">false</span>;</span><br><span class="line">            mPeekTouching = mPanelClosedOnDown;</span><br><span class="line">            mDownTime = SystemClock.uptimeMillis();</span><br><span class="line">            mTouchAboveFalsingThreshold = <span class="keyword">false</span>;</span><br><span class="line">            mCollapsedAndHeadsUpOnDown = isFullyCollapsed()</span><br><span class="line">                    &amp;&amp; mHeadsUpManager.hasPinnedHeadsUp();</span><br><span class="line">            <span class="keyword">if</span> (mVelocityTracker == <span class="keyword">null</span>) &#123;</span><br><span class="line">                initVelocityTracker();</span><br><span class="line">            &#125;</span><br><span class="line">            trackMovement(event);</span><br><span class="line">            <span class="keyword">if</span> (!mGestureWaitForTouchSlop || (mHeightAnimator != <span class="keyword">null</span> &amp;&amp; !mHintAnimationRunning)</span><br><span class="line">                    || mPeekAnimator != <span class="keyword">null</span>) &#123;</span><br><span class="line">                mTouchSlopExceeded = (mHeightAnimator != <span class="keyword">null</span> &amp;&amp; !mHintAnimationRunning)</span><br><span class="line">                        || mPeekAnimator != <span class="keyword">null</span>;</span><br><span class="line">                cancelHeightAnimator();</span><br><span class="line">                cancelPeek();</span><br><span class="line">                onTrackingStarted();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (isFullyCollapsed() &amp;&amp; !mHeadsUpManager.hasPinnedHeadsUp()) &#123;</span><br><span class="line">                startOpening();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_POINTER_UP:</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">int</span> upPointer = event.getPointerId(event.getActionIndex());</span><br><span class="line">            <span class="keyword">if</span> (mTrackingPointer == upPointer) &#123;</span><br><span class="line">                <span class="comment">// gesture is ongoing, find a new pointer to track</span></span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> newIndex = event.getPointerId(<span class="number">0</span>) != upPointer ? <span class="number">0</span> : <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">float</span> newY = event.getY(newIndex);</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">float</span> newX = event.getX(newIndex);</span><br><span class="line">                mTrackingPointer = event.getPointerId(newIndex);</span><br><span class="line">                startExpandMotion(newX, newY, <span class="keyword">true</span> <span class="comment">/* startTracking */</span>, mExpandedHeight);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_POINTER_DOWN:</span><br><span class="line">            <span class="keyword">if</span> (mStatusBar.getBarState() == StatusBarState.KEYGUARD) &#123;</span><br><span class="line">                mMotionAborted = <span class="keyword">true</span>;</span><br><span class="line">                endMotionEvent(event, x, y, <span class="keyword">true</span> <span class="comment">/* forceCancel */</span>);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_MOVE:</span><br><span class="line">            trackMovement(event);</span><br><span class="line">            <span class="keyword">float</span> h = y - mInitialTouchY;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If the panel was collapsed when touching, we only need to check for the</span></span><br><span class="line">            <span class="comment">// y-component of the gesture, as we have no conflicting horizontal gesture.</span></span><br><span class="line">            <span class="keyword">if</span> (Math.abs(h) &gt; mTouchSlop</span><br><span class="line">                    &amp;&amp; (Math.abs(h) &gt; Math.abs(x - mInitialTouchX)</span><br><span class="line">                    || mIgnoreXTouchSlop)) &#123;</span><br><span class="line">                mTouchSlopExceeded = <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">if</span> (mGestureWaitForTouchSlop &amp;&amp; !mTracking &amp;&amp; !mCollapsedAndHeadsUpOnDown) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!mJustPeeked &amp;&amp; mInitialOffsetOnTouch != <span class="number">0f</span>) &#123;</span><br><span class="line">                        startExpandMotion(x, y, <span class="keyword">false</span> <span class="comment">/* startTracking */</span>, mExpandedHeight);</span><br><span class="line">                        h = <span class="number">0</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    cancelHeightAnimator();</span><br><span class="line">                    onTrackingStarted();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">float</span> newHeight = Math.max(<span class="number">0</span>, h + mInitialOffsetOnTouch);</span><br><span class="line">            <span class="keyword">if</span> (newHeight &gt; mPeekHeight) &#123;</span><br><span class="line">                <span class="keyword">if</span> (mPeekAnimator != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    mPeekAnimator.cancel();</span><br><span class="line">                &#125;</span><br><span class="line">                mJustPeeked = <span class="keyword">false</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mPeekAnimator == <span class="keyword">null</span> &amp;&amp; mJustPeeked) &#123;</span><br><span class="line">                <span class="comment">// The initial peek has finished, but we haven't dragged as far yet, lets</span></span><br><span class="line">                <span class="comment">// speed it up by starting at the peek height.</span></span><br><span class="line">                mInitialOffsetOnTouch = mExpandedHeight;</span><br><span class="line">                mInitialTouchY = y;</span><br><span class="line">                mMinExpandHeight = mExpandedHeight;</span><br><span class="line">                mJustPeeked = <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            newHeight = Math.max(newHeight, mMinExpandHeight);</span><br><span class="line">            <span class="keyword">if</span> (-h &gt;= getFalsingThreshold()) &#123;</span><br><span class="line">                mTouchAboveFalsingThreshold = <span class="keyword">true</span>;</span><br><span class="line">                mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!mJustPeeked &amp;&amp; (!mGestureWaitForTouchSlop || mTracking) &amp;&amp;</span><br><span class="line">                    !isTrackingBlocked()) &#123;</span><br><span class="line">                setExpandedHeightInternal(newHeight);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_UP:</span><br><span class="line">        <span class="keyword">case</span> MotionEvent.ACTION_CANCEL:</span><br><span class="line">            trackMovement(event);</span><br><span class="line">            endMotionEvent(event, x, y, <span class="keyword">false</span> <span class="comment">/* forceCancel */</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> !mGestureWaitForTouchSlop || mTracking;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> endMotionEvent()<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">endMotionEvent</span><span class="params">(MotionEvent event, <span class="keyword">float</span> x, <span class="keyword">float</span> y, <span class="keyword">boolean</span> forceCancel)</span> </span>&#123;</span><br><span class="line">    mTrackingPointer = -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> ((mTracking &amp;&amp; mTouchSlopExceeded)</span><br><span class="line">            || Math.abs(x - mInitialTouchX) &gt; mTouchSlop</span><br><span class="line">            || Math.abs(y - mInitialTouchY) &gt; mTouchSlop</span><br><span class="line">            || event.getActionMasked() == MotionEvent.ACTION_CANCEL</span><br><span class="line">            || forceCancel) &#123;</span><br><span class="line">        <span class="keyword">float</span> vel = <span class="number">0f</span>;</span><br><span class="line">        <span class="keyword">float</span> vectorVel = <span class="number">0f</span>;</span><br><span class="line">        <span class="keyword">if</span> (mVelocityTracker != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mVelocityTracker.computeCurrentVelocity(<span class="number">1000</span>);<span class="comment">//设置计算速度的单位是1000ms,即1s</span></span><br><span class="line">            vel = mVelocityTracker.getYVelocity();  <span class="comment">//计算竖直方向的速度</span></span><br><span class="line">            vectorVel = (<span class="keyword">float</span>) Math.hypot(</span><br><span class="line">                    mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());  <span class="comment">//计算加速度</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">boolean</span> expand = flingExpands(vel, vectorVel, x, y)</span><br><span class="line">                || event.getActionMasked() == MotionEvent.ACTION_CANCEL</span><br><span class="line">                || forceCancel;</span><br><span class="line">        DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,</span><br><span class="line">                mStatusBar.isFalsingThresholdNeeded(),</span><br><span class="line">                mStatusBar.isWakeUpComingFromTouch());</span><br><span class="line">                <span class="comment">// Log collapse gesture if on lock screen. // 如果未下拉通知面板，且处于锁屏界面，记录log</span></span><br><span class="line">                <span class="keyword">if</span> (!expand &amp;&amp; mStatusBar.getBarState() == StatusBarState.KEYGUARD) &#123;</span><br><span class="line">                    <span class="keyword">float</span> displayDensity = mStatusBar.getDisplayDensity();</span><br><span class="line">                    <span class="keyword">int</span> heightDp = (<span class="keyword">int</span>) Math.abs((y - mInitialTouchY) / displayDensity);</span><br><span class="line">                    <span class="keyword">int</span> velocityDp = (<span class="keyword">int</span>) Math.abs(vel / displayDensity);</span><br><span class="line">                    mLockscreenGestureLogger.write(</span><br><span class="line">                            MetricsEvent.ACTION_LS_UNLOCK,</span><br><span class="line">                            heightDp, velocityDp);</span><br><span class="line">                &#125;</span><br><span class="line">        fling(vel, expand, isFalseTouch(x, y));</span><br><span class="line">        onTrackingStopped(expand);</span><br><span class="line">        mUpdateFlingOnLayout = expand &amp;&amp; mPanelClosedOnDown &amp;&amp; !mHasLayoutedSinceDown;</span><br><span class="line">        <span class="keyword">if</span> (mUpdateFlingOnLayout) &#123;</span><br><span class="line">            mUpdateFlingVelocity = vel;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mPanelClosedOnDown &amp;&amp; !mHeadsUpManager.hasPinnedHeadsUp() &amp;&amp; !mTracking) &#123;</span><br><span class="line">        <span class="keyword">long</span> timePassed = SystemClock.uptimeMillis() - mDownTime;</span><br><span class="line">        <span class="keyword">if</span> (timePassed &lt; ViewConfiguration.getLongPressTimeout()) &#123;</span><br><span class="line">            <span class="comment">// Lets show the user that he can actually expand the panel</span></span><br><span class="line">            runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), <span class="keyword">true</span> <span class="comment">/* collapseWhenFinished */</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// We need to collapse the panel since we peeked to the small height.</span></span><br><span class="line">            postOnAnimation(mPostCollapseRunnable);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">boolean</span> expands = onEmptySpaceClick(mInitialTouchX);</span><br><span class="line">        onTrackingStopped(expands);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mVelocityTracker != <span class="keyword">null</span>) &#123;</span><br><span class="line">        mVelocityTracker.recycle();</span><br><span class="line">        mVelocityTracker = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    mPeekTouching = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> • 通过对mVelocityTracker变量，VelocityTrackerInterface初始化时velocity_tracker_impl值为platform，确定使用的是PlatformVelocityTracker来计算滑动速度.</p><h2 id="计算速度相关"><a href="#计算速度相关" class="headerlink" title="计算速度相关"></a>计算速度相关</h2><p> 速度跟踪接口<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/VelocityTrackerInterface.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * An interface for a velocity tracker to delegate. To be implemented by different velocity tracking</span></span><br><span class="line"><span class="comment"> * algorithms.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">VelocityTrackerInterface</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addMovement</span><span class="params">(MotionEvent event)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">computeCurrentVelocity</span><span class="params">(<span class="keyword">int</span> units)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getXVelocity</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getYVelocity</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">recycle</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 速度跟踪工厂类，速度跟踪有两个类<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/VelocityTrackerFactory.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.android.systemui.statusbar.phone;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.android.systemui.R;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * A class to generate &#123;<span class="doctag">@link</span> VelocityTrackerInterface&#125;, depending on the configuration.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">VelocityTrackerFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String PLATFORM_IMPL = <span class="string">"platform"</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String NOISY_IMPL = <span class="string">"noisy"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> VelocityTrackerInterface <span class="title">obtain</span><span class="params">(Context ctx)</span> </span>&#123;</span><br><span class="line">        String tracker = ctx.getResources().getString(R.string.velocity_tracker_impl);</span><br><span class="line">        <span class="keyword">switch</span> (tracker) &#123;</span><br><span class="line">            <span class="keyword">case</span> NOISY_IMPL:</span><br><span class="line">                <span class="keyword">return</span> NoisyVelocityTracker.obtain();</span><br><span class="line">            <span class="keyword">case</span> PLATFORM_IMPL:</span><br><span class="line">                <span class="keyword">return</span> PlatformVelocityTracker.obtain();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Invalid tracker: "</span> + tracker);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="解锁认证"><a href="#解锁认证" class="headerlink" title="解锁认证"></a>解锁认证</h1><p>在接下来要讲的几种解锁方式：图案解锁、PIN码解锁、密码解锁、指纹解锁、面部解锁等等。均存在一个问题，那就是解锁校验问题.</p><h1 id="图案解锁"><a href="#图案解锁" class="headerlink" title="图案解锁"></a>图案解锁</h1><h2 id="九宫格解锁的实现"><a href="#九宫格解锁的实现" class="headerlink" title="九宫格解锁的实现"></a>九宫格解锁的实现</h2><h1 id="PIN码解锁"><a href="#PIN码解锁" class="headerlink" title="PIN码解锁"></a>PIN码解锁</h1><h1 id="密码解锁"><a href="#密码解锁" class="headerlink" title="密码解锁"></a>密码解锁</h1><h2 id="密码自动校验"><a href="#密码自动校验" class="headerlink" title="密码自动校验"></a>密码自动校验</h2><p>在密码解锁界面，用户输入4位以及4位以上密码时，开始自动校验，如果输入正确，则直接解锁，否则让用户继续输入.</p><h1 id="指纹解锁"><a href="#指纹解锁" class="headerlink" title="指纹解锁"></a>指纹解锁</h1><h1 id="面部解锁"><a href="#面部解锁" class="headerlink" title="面部解锁"></a>面部解锁</h1><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/lintcgirl/article/details/51500284" target="_blank" rel="noopener">Android 6.0 ConfirmCredential</a></p><p><a href="https://blog.csdn.net/ocean2006/article/details/8079457" target="_blank" rel="noopener">Android4.0 Keyguard解锁屏机制</a><br><a href="https://blog.csdn.net/maetelibom/article/details/78131169" target="_blank" rel="noopener">Android 7.0 锁屏解锁之向上滑动显示解锁界面分析</a></p><p><a href="https://www.cnblogs.com/haiming/p/2989678.html" target="_blank" rel="noopener">Android Framework——之Keyguard 简单分析</a><br><a href="https://blog.csdn.net/yj934672573/article/details/54571704" target="_blank" rel="noopener">Android 7.0 Keyguard流程分析</a></p><p><a href="https://www.2cto.com/kf/201711/698492_2.html" target="_blank" rel="noopener">Android 7.0 Keyguard流程分析</a> GateKeeper模块的解析<br><a href="https://blog.csdn.net/dxpqxb/article/details/53607586" target="_blank" rel="noopener">Android窗口系统机制之KEYGUARD机制</a> Keyguard模块的框架<br><a href="https://blog.csdn.net/wzy_1988/article/details/49659935" target="_blank" rel="noopener">AndroidL 开机展示Keyguard锁屏机制初探</a><br><a href="https://blog.csdn.net/vrix/article/details/39204013" target="_blank" rel="noopener">Android4.0 Keyguard解锁屏机制</a> 带流程图</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt; 本文是基于Android版本：8.1.0来讨论Keyguard相关代码.&lt;/p&gt;
&lt;p&gt; 在Android 8.1.0版本中，将Keygu
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Keyguard" scheme="http://yoursite.com/categories/Android/Keyguard/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Keyguard" scheme="http://yoursite.com/tags/Keyguard/"/>
    
  </entry>
  
  <entry>
    <title>ANR 机制以及问题分析</title>
    <link href="http://yoursite.com/Android-ANR-Analysis/"/>
    <id>http://yoursite.com/Android-ANR-Analysis/</id>
    <published>2018-05-01T11:06:22.000Z</published>
    <updated>2018-08-07T06:55:15.734Z</updated>
    
    <content type="html"><![CDATA[<p>本文目标讲解Android中的ANR原理以及如何分析ANR问题</p><h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>   ANR(Application Not Responding),应用程序无响应，简单的一个定义，却涵盖了很多AAndroid系统的设计思想。</p><p>   首先，ANR属于应用程序范畴，不同于SNR(System Not Responding),SNR反映的问题是系统进程(system_server)失去了响应能力，而ANR明确将问题圈定在应用程序。SNR是有Watchdog机制保证，具体可查阅<strong>Watchdog机制以及问题分析</strong>;ANR由消息处理机制保证，Android在系统层实现了一套机制来发现ANR,核心原理是消息调度和超时处理。</p><p>   其次，ANR机制的主体实现在系统层。所有与ANR相关的消息，都会经过系统进程(system_server)调度，然后派发发哦应用进程完成对消息的实际处理，同时，系统进程设计了不同的超时限制来跟踪消息的处理。一旦应用程序处理消息不当，超时限制就起了作用了，它收集一些系统状态，比如CPU/IO使用情况、进程函数调用栈，并报告用户进程无响应(弹出ANR对话框).</p><p>   然后，ANR问题本质是一个性能问题和稳定性的问题。ANR机制实际上对应用程序主线程的限制，要求主线程在限定的时间内处理完一些常见的操作(启动服务、处理广播、处理输入)，如果超时，则任务主线程已经失去了响应其他操作的能力。主线程中的耗时操作，比如密集CPU运算、大量IO、复杂界面布局等，都会降低应用程序的响应能力。当系统中的ANR问题太多的时候，就影响到了系统的稳定性。</p><p>   最后，部分ANR问题时很难分析的，有时候由于系统底层的一些影响，导致消息调度失败，比如说Binder资源耗尽，nativepollonce等，出现问题的场景又难以复现。这类ANR问题往往需要花费大量的事件去了解系统的一些行为，超出了ANR机制本省的范畴。</p><p>   出现ANR的场景主要有以下几种：</p><p>   • <em>  <strong>Service Timeout:</strong>服务在20s内未执行完成；<br>   • </em>  <strong>BroadcastQueue Timeout：</strong>比如前台广播在10s内执行完成<br>   • <em>  <strong>ContentProvider Timeout：</strong>内容提供者执行超时<br>   • </em>  <strong>inputDispatching Timeout:</strong> 输入事件分发超时5s，包括按键分发事件的超时</p><h2 id="Google-官方关于ANR的介绍"><a href="#Google-官方关于ANR的介绍" class="headerlink" title="Google 官方关于ANR的介绍"></a>Google 官方关于ANR的介绍</h2><p>   Google官方关于ANR的介绍是在介绍使用Android Vitals工具时提到：<a href="https://developer.android.com/topic/performance/vitals/anr" target="_blank" rel="noopener">https://developer.android.com/topic/performance/vitals/anr</a></p><h3 id="查找ANR根因"><a href="#查找ANR根因" class="headerlink" title="查找ANR根因"></a>查找ANR根因</h3><p>查找ANR根因的方法有</p><ol><li><p>使用StrictMode<br>具体请参照： StrictMode 机制以及性能调优</p></li><li><p>启动ANR 背景对话框<br>Enable background ANR dialogs</p><p>Android shows ANR dialogs for apps that take too long to process the broadcast message only if Show all ANRs is enabled in the device’s Developer options. For this reason, background ANR dialogs are not always displayed to the user, but the app could still be experiencing performance issues.</p><p> 打开 开发者模式 –&gt; 打开 Show All ANR</p></li><li><p>使用TraceView</p></li><li><p>adb pull data/anr</p></li></ol><h3 id="解决ANR问题的方式"><a href="#解决ANR问题的方式" class="headerlink" title="解决ANR问题的方式"></a>解决ANR问题的方式</h3><pre><code>There are some common patterns to look for when diagnosing ANRs:1. The app is doing slow operations involving I/O on the main thread.2. The app is doing a long calculation on the main thread.3. The main thread is doing a synchronous binder call to another process, and that other process is taking a long time to return.4. The main thread is blocked waiting for a synchronized block for a long operation that is happening on another thread.5. The main thread is in a deadlock with another thread, either in your process or via a binder call. The main thread is not just waiting for a long operation to finish, but is in a deadlock situation. For more information, see Deadlock on Wikipedia.</code></pre><p>翻译如下:<br>    在诊断ANR时有一些常见的模式需要查找：</p><pre><code>1. 该应用程序在主线程上执行涉及I / O的缓慢操作。2. 该应用程序正在对主线程进行长时间计算。3. 主线程正在对另一个进程执行同步绑定程序调用，而其他进程需要很长时间才能返回。4. 主线程被阻塞，等待正在另一个线程上发生的长操作的同步块。5. 主线程与另一个线程处于死锁状态，无论是在您的进程中还是通过联编程序调用。主线不仅仅是等待长时间的操作才能完成，而是处于死锁状态。有关更多信息，请参阅 维基百科上的死锁。</code></pre><ol><li><p>主线程执行超时<br>定位到是主线程超时时，需要找到对应的代码，将超时操作移到<a href="https://developer.android.com/topic/performance/threads#helper" target="_blank" rel="noopener">Handler</a>里面去处理，或者使用AsyncTask(<a href="https://developer.android.com/reference/android/os/AsyncTask" target="_blank" rel="noopener">https://developer.android.com/reference/android/os/AsyncTask</a>) 异步任务的方式来处理。</p></li><li><p>主线程I/O超时<br>I/O超时，将超时操作移动到线程中处理,使用Handler或者AsyncTask.如果I/O是网络操作的或者存储操作的话，<a href="https://developer.android.com/training/basics/network-ops/" target="_blank" rel="noopener">网络操作</a>使用<a href="https://developer.android.com/training/volley/" target="_blank" rel="noopener">Volly</a>的方式来实现,数据存储的话，参照Android提供的几种存储方式<a href="https://developer.android.com/guide/topics/data/data-storage" target="_blank" rel="noopener">https://developer.android.com/guide/topics/data/data-storage</a></p></li><li><p>互斥锁<br>当前工作线程持有了UI线程所需要的资源太长时间，导致UI线程一直处于Wait状态.UI线程被阻塞住。可以使用<a href="https://developer.android.com/reference/java/util/concurrent/Semaphore" target="_blank" rel="noopener">Semaphore</a>(计数信号量)或者线程<a href="https://developer.android.com/reference/java/util/concurrent/locks/Lock" target="_blank" rel="noopener">锁</a>等其他的互斥机制来解决。 </p></li><li><p>死锁<br>UI线程和其他线程互相持有对方所需要的资源，导致死锁。双方都在等待对方释放资源。<br>解决死锁的方式：</p></li><li><p>广播超时<br>广播OnReceiver方法中耗时太久，比如前台广播超过10s未完成等<br>如果广播中需要执行复杂的操作，可以通过<a href="">IntenService</a>来实现。</p></li><li><p>服务超时/内容提供者超时/输入事件超时<br>都是用Handler或者AsyncTask来处理</p></li><li><p>CPU负载过高<br>一般CPU负载过高，只有在进行压力测试的时候才能出现，出现CPU负载过高之后，会使得很多事件得不到响应。一般CPU负载过高的ANR问题都不解。   </p></li></ol><h2 id="ANR-机制"><a href="#ANR-机制" class="headerlink" title="ANR 机制"></a>ANR 机制</h2><p>   分析一些初级的ANR问题，只需要简单理解最终输出的日志即可，但对于一些系统问题(比如CPU负载过高、进程卡死) 发的ANR，就需要对整个ANR机制有所了解，才能定位出问题的原因。</p><p>   ANR机制可以分为两部分：</p><p>   • *  <strong>ANR的监测。</strong> Android对不同的ANR类型(Broadcase/Service/InputEvent)都有一套监测机制。</p><p>   • *  <strong>ANR的报告。</strong> 在监测到ANR以后，需要显示ANR对话框、输出日志(发生ANR时的进程函数调用栈、CPU使用情况等)。</p><p>   整个ANR机制的代码也是横跨了Android的几个层：</p><p>   • *  <strong>App层：</strong>应用主线程的处理逻辑</p><p>   • *  <strong>Framework层：</strong>  ANR机制的核心</p><blockquote><ul><li>frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java</li><li>frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java</li><li>frameworks/base/services/core/java/com/android/server/am/ActiveServices.java</li><li>frameworks/base/services/core/java/com/android/server/input/InputManagerService.java</li><li>frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java</li><li>frameworks/base/core/java/android/view/InputChannel</li><li>frameworks/base/services/core/java/com/android/internal/os/ProcessCpuTracker</li></ul></blockquote><p>   • * <strong>Native层：</strong> 输入事件派发机制。针对InputEvent类型的ANR</p><blockquote><ul><li>frameworks/base//services/core/jni/com_android_server_input_InputManagerService.cpp</li><li>frameworks/native/services/inputflinger/InputDispatcher.cpp</li></ul></blockquote><p>   下面我们会深入源码，分析ANR的监测和报告过程。</p><h3 id="ANR检测机制"><a href="#ANR检测机制" class="headerlink" title="ANR检测机制"></a>ANR检测机制</h3><h4 id="Service-处理超时"><a href="#Service-处理超时" class="headerlink" title="Service 处理超时"></a>Service 处理超时</h4><p>   Service运行在应用程序的主线程，如果Service的执行时间超过20秒，则会引发ANR。</p><p>   当发生Service ANR时，一般可以先排查一下在Service的生命周期函数中(onCreate(), onStartCommand()等)有没有做耗时的操作，譬如复杂的运算、IO操作等。如果应用程序的代码逻辑查不出问题，就需要深入检查当前系统的状态：CPU的使用情况、系统服务的状态等，判断当时发生ANR进程是否受到系统运行异常的影响。</p><p>   如何检测Service超时呢？Android是通过设置定时消息实现的。定时消息是由AMS的消息队列处理的(system_server的ActivityManager线程)。 AMS有Service运行的上下文信息，所以在AMS中设置一套超时检测机制也是合情合理的。</p><p>   Service ANR机制相对最为简单，简单说就是AMS中的mHandler收到SERVICE_TIMEOUT_MSG消息时触发。<br>   主体实现在<a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java" target="_blank" rel="noopener">ActiveServices.java</a>中。 当Service的生命周期开始时，bumpServiceExecutingLocked()会被调用，紧接着会调用scheduleServiceTimeoutLocked()：</p><p>   StartService的时候：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">realStartServiceLocked</span><span class="params">(ServiceRecord r,</span></span></span><br><span class="line"><span class="function"><span class="params">            ProcessRecord app, <span class="keyword">boolean</span> execInFg)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (app.thread == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RemoteException();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (DEBUG_MU)</span><br><span class="line">            Slog.v(TAG_MU, <span class="string">"realStartServiceLocked, ServiceRecord.uid = "</span> + r.appInfo.uid</span><br><span class="line">                    + <span class="string">", ProcessRecord.uid = "</span> + app.uid);</span><br><span class="line">        r.app = app;</span><br><span class="line">        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> newService = app.services.add(r);</span><br><span class="line">        bumpServiceExecutingLocked(r, execInFg, <span class="string">"create"</span>);<span class="comment">//执行bumpServiceExecutingLocked</span></span><br><span class="line">        mAm.updateLruProcessLocked(app, <span class="keyword">false</span>, <span class="keyword">null</span>);</span><br><span class="line">        updateServiceForegroundLocked(r.app, <span class="comment">/* oomAdj= */</span> <span class="keyword">false</span>);</span><br><span class="line">        mAm.updateOomAdjLocked();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> created = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (LOG_SERVICE_START_STOP) &#123;</span><br><span class="line">                String nameTerm;</span><br><span class="line">                <span class="keyword">int</span> lastPeriod = r.shortName.lastIndexOf(<span class="string">'.'</span>);</span><br><span class="line">                nameTerm = lastPeriod &gt;= <span class="number">0</span> ? r.shortName.substring(lastPeriod) : r.shortName;</span><br><span class="line">                EventLogTags.writeAmCreateService(</span><br><span class="line">                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);</span><br><span class="line">            &#125;</span><br><span class="line">            ........</span><br></pre></td></tr></table></figure></p><p>   BindService的时候：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">requestServiceBindingLocked</span><span class="params">(ServiceRecord r, IntentBindRecord i,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> execInFg, <span class="keyword">boolean</span> rebind)</span> <span class="keyword">throws</span> TransactionTooLargeException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (r.app == <span class="keyword">null</span> || r.app.thread == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// If service is not currently running, can't yet bind.</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (DEBUG_SERVICE) Slog.d(TAG_SERVICE, <span class="string">"requestBind "</span> + i + <span class="string">": requested="</span> + i.requested</span><br><span class="line">                + <span class="string">" rebind="</span> + rebind);</span><br><span class="line">        <span class="keyword">if</span> ((!i.requested || rebind) &amp;&amp; i.apps.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                bumpServiceExecutingLocked(r, execInFg, <span class="string">"bind"</span>);<span class="comment">//执行bumpServiceExecutingLocked</span></span><br><span class="line">                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);</span><br><span class="line">                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,</span><br><span class="line">                        r.app.repProcState);</span><br><span class="line">                <span class="keyword">if</span> (!rebind) &#123;</span><br><span class="line">                    i.requested = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                i.hasBound = <span class="keyword">true</span>;</span><br><span class="line">                i.doRebind = <span class="keyword">false</span>;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (TransactionTooLargeException e) &#123;</span><br><span class="line">                <span class="comment">// Keep the executeNesting count accurate.</span></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG_SERVICE, <span class="string">"Crashed while binding "</span> + r, e);</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">boolean</span> inDestroying = mDestroyingServices.contains(r);</span><br><span class="line">                serviceDoneExecutingLocked(r, inDestroying, inDestroying);</span><br><span class="line">                <span class="keyword">throw</span> e;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG_SERVICE, <span class="string">"Crashed while binding "</span> + r);</span><br><span class="line">                <span class="comment">// Keep the executeNesting count accurate.</span></span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">boolean</span> inDestroying = mDestroyingServices.contains(r);</span><br><span class="line">                serviceDoneExecutingLocked(r, inDestroying, inDestroying);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>调用bumpServiceExecutingLocked</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">bumpServiceExecutingLocked</span><span class="params">(ServiceRecord r, <span class="keyword">boolean</span> fg, String why)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG_SERVICE, <span class="string">"&gt;&gt;&gt; EXECUTING "</span></span><br><span class="line">            + why + <span class="string">" of "</span> + r + <span class="string">" in app "</span> + r.app);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, <span class="string">"&gt;&gt;&gt; EXECUTING "</span></span><br><span class="line">            + why + <span class="string">" of "</span> + r.shortName);</span><br><span class="line">    <span class="keyword">long</span> now = SystemClock.uptimeMillis();</span><br><span class="line">    <span class="keyword">if</span> (r.executeNesting == <span class="number">0</span>) &#123;</span><br><span class="line">        r.executeFg = fg;</span><br><span class="line">        ServiceState stracker = r.getTracker();</span><br><span class="line">        <span class="keyword">if</span> (stracker != <span class="keyword">null</span>) &#123;</span><br><span class="line">            stracker.setExecuting(<span class="keyword">true</span>, mAm.mProcessStats.getMemFactorLocked(), now);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (r.app != <span class="keyword">null</span>) &#123;</span><br><span class="line">            r.app.executingServices.add(r);</span><br><span class="line">            r.app.execServicesFg |= fg;</span><br><span class="line">            <span class="keyword">if</span> (r.app.executingServices.size() == <span class="number">1</span>) &#123;</span><br><span class="line">                scheduleServiceTimeoutLocked(r.app);<span class="comment">//</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (r.app != <span class="keyword">null</span> &amp;&amp; fg &amp;&amp; !r.app.execServicesFg) &#123;</span><br><span class="line">        r.app.execServicesFg = <span class="keyword">true</span>;</span><br><span class="line">        scheduleServiceTimeoutLocked(r.app);<span class="comment">//</span></span><br><span class="line">    &#125;</span><br><span class="line">    r.executeFg |= fg;</span><br><span class="line">    r.executeNesting++;</span><br><span class="line">    r.executingStart = now;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>紧接着调用scheduleServiceTimeoutLocked<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">scheduleServiceTimeoutLocked</span><span class="params">(ProcessRecord proc)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (proc.executingServices.size() == <span class="number">0</span> || proc.thread == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Message msg = mAm.mHandler.obtainMessage(</span><br><span class="line">            ActivityManagerService.SERVICE_TIMEOUT_MSG);</span><br><span class="line">    msg.obj = proc;</span><br><span class="line">    mAm.mHandler.sendMessageDelayed(msg,</span><br><span class="line">            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上述方法通过AMS.MainHandler抛出一个定时消息SERVICE_TIMEOUT_MSG：</p><p>   • <em>  <strong>前台进程中执行Service</strong>，超时时间是SERVICE_TIMEOUT(20秒)<br>   • </em>  <strong>后台进程中执行Service</strong>，超时时间是SERVICE_BACKGROUND_TIMEOUT(200秒)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// How long we wait for a service to finish executing.</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SERVICE_TIMEOUT = <span class="number">20</span>*<span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// How long we wait for a service to finish executing.</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// How long the startForegroundService() grace period is to get around to</span></span><br><span class="line"><span class="comment">// calling startForeground() before we ANR + stop it.</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SERVICE_START_FOREGROUND_TIMEOUT = <span class="number">5</span>*<span class="number">1000</span>;</span><br></pre></td></tr></table></figure><p>当Service的生命周期结束时，会调用serviceDoneExecutingLocked()方法，之前抛出的SERVICE_TIMEOUT_MSG消息在这个方法中会被清除。 如果在超时时间内，SERVICE_TIMEOUT_MSG没有被清除，那么，AMS.MainHandler就会响应这个消息:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">serviceDoneExecutingLocked</span><span class="params">(ServiceRecord r, <span class="keyword">boolean</span> inDestroying,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> finishing)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG_SERVICE, <span class="string">"&lt;&lt;&lt; DONE EXECUTING "</span> + r</span><br><span class="line">                + <span class="string">": nesting="</span> + r.executeNesting</span><br><span class="line">                + <span class="string">", inDestroying="</span> + inDestroying + <span class="string">", app="</span> + r.app);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,</span><br><span class="line">                <span class="string">"&lt;&lt;&lt; DONE EXECUTING "</span> + r.shortName);</span><br><span class="line">        r.executeNesting--;</span><br><span class="line">        <span class="keyword">if</span> (r.executeNesting &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (r.app != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_SERVICE) Slog.v(TAG_SERVICE,</span><br><span class="line">                        <span class="string">"Nesting at 0 of "</span> + r.shortName);</span><br><span class="line">                r.app.execServicesFg = <span class="keyword">false</span>;</span><br><span class="line">                r.app.executingServices.remove(r);</span><br><span class="line">                <span class="keyword">if</span> (r.app.executingServices.size() == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,</span><br><span class="line">                            <span class="string">"No more executingServices of "</span> + r.shortName);</span><br><span class="line">                    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);<span class="comment">//清除SERVICE_TIMEOUT_MSG消息</span></span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (r.executeFg) &#123;</span><br><span class="line">                    <span class="comment">// Need to re-evaluate whether the app still needs to be in the foreground.</span></span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> i=r.app.executingServices.size()-<span class="number">1</span>; i&gt;=<span class="number">0</span>; i--) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (r.app.executingServices.valueAt(i).executeFg) &#123;</span><br><span class="line">                            r.app.execServicesFg = <span class="keyword">true</span>;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                .....</span><br></pre></td></tr></table></figure><p>AMS.MainHandler就会响应这个消息:<br>见<a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActivityManagerService.java" target="_blank" rel="noopener">ActivityManagerService</a><br>本文是基于Android8.0的代码分析，与<a href="http://duanqz.github.io/2015-10-12-ANR-Analysis" target="_blank" rel="noopener">http://duanqz.github.io/2015-10-12-ANR-Analysis</a> 这篇文章这里的代码不一样。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">MainHandler</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="title">MainHandler</span><span class="params">(Looper looper)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">super</span>(looper, <span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">           <span class="keyword">case</span> UPDATE_CONFIGURATION_MSG: &#123;</span><br><span class="line">               <span class="keyword">final</span> ContentResolver resolver = mContext.getContentResolver();</span><br><span class="line">               Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,</span><br><span class="line">                       msg.arg1);</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           <span class="keyword">case</span> GC_BACKGROUND_PROCESSES_MSG: &#123;</span><br><span class="line">               <span class="keyword">synchronized</span> (ActivityManagerService.<span class="keyword">this</span>) &#123;</span><br><span class="line">                   performAppGcsIfAppropriateLocked();</span><br><span class="line">               &#125;</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           <span class="keyword">case</span> SERVICE_TIMEOUT_MSG: &#123;  </span><br><span class="line">               mServices.serviceTimeout((ProcessRecord)msg.obj);</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           <span class="keyword">case</span> SERVICE_FOREGROUND_TIMEOUT_MSG: &#123;</span><br><span class="line">               mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           <span class="keyword">case</span> SERVICE_FOREGROUND_CRASH_MSG: &#123;</span><br><span class="line">               mServices.serviceForegroundCrash((ProcessRecord)msg.obj);</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           <span class="keyword">case</span> DISPATCH_PENDING_INTENT_CANCEL_MSG: &#123;</span><br><span class="line">               RemoteCallbackList&lt;IResultReceiver&gt; callbacks</span><br><span class="line">                       = (RemoteCallbackList&lt;IResultReceiver&gt;)msg.obj;</span><br><span class="line">               <span class="keyword">int</span> N = callbacks.beginBroadcast();</span><br><span class="line">               <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; N; i++) &#123;</span><br><span class="line">                   <span class="keyword">try</span> &#123;</span><br><span class="line">                       callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, <span class="keyword">null</span>);</span><br><span class="line">                   &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;</span><br><span class="line">               callbacks.finishBroadcast();</span><br><span class="line">           &#125; <span class="keyword">break</span>;</span><br><span class="line">           ......</span><br></pre></td></tr></table></figure></p><p>mServices是ActiveServices，又返回到ActiveService中进行处理:<br>serviceTimeout 的处理如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">serviceTimeout</span><span class="params">(ProcessRecord proc)</span> </span>&#123;</span><br><span class="line">    String anrMessage = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">synchronized</span>(mAm) &#123;</span><br><span class="line">        <span class="keyword">if</span> (proc.executingServices.size() == <span class="number">0</span> || proc.thread == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">long</span> now = SystemClock.uptimeMillis();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">long</span> maxTime =  now -</span><br><span class="line">                (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);</span><br><span class="line">        ServiceRecord timeout = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">long</span> nextTime = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i=proc.executingServices.size()-<span class="number">1</span>; i&gt;=<span class="number">0</span>; i--) &#123;<span class="comment">// 寻找运行超时的Service</span></span><br><span class="line">            ServiceRecord sr = proc.executingServices.valueAt(i);</span><br><span class="line">            <span class="keyword">if</span> (sr.executingStart &lt; maxTime) &#123;</span><br><span class="line">                timeout = sr;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (sr.executingStart &gt; nextTime) &#123;</span><br><span class="line">                nextTime = sr.executingStart;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (timeout != <span class="keyword">null</span> &amp;&amp; mAm.mLruProcesses.contains(proc)) &#123; <span class="comment">// 判断执行Service超时的进程是否在最近运行进程列表，如果在，则输出log到logcat和dumplog中，并记录anrMessage供弹框使用</span></span><br><span class="line">            Slog.w(TAG, <span class="string">"Timeout executing service: "</span> + timeout);</span><br><span class="line">            StringWriter sw = <span class="keyword">new</span> StringWriter();</span><br><span class="line">            PrintWriter pw = <span class="keyword">new</span> FastPrintWriter(sw, <span class="keyword">false</span>, <span class="number">1024</span>);</span><br><span class="line">            pw.println(timeout);</span><br><span class="line">            timeout.dump(pw, <span class="string">"    "</span>);</span><br><span class="line">            pw.close();</span><br><span class="line">            mLastAnrDump = sw.toString();</span><br><span class="line">            mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);</span><br><span class="line">            mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);</span><br><span class="line">            anrMessage = <span class="string">"executing service "</span> + timeout.shortName;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;  <span class="comment">//其他的三种情况分别如下，则继续等待监测20s或者200s</span></span><br><span class="line">        <span class="comment">//a)如果Service超时，但是不在最近运行进程列表中</span></span><br><span class="line">        <span class="comment">//b)如果Service不超时，且不在最近运行进程列表中</span></span><br><span class="line">        <span class="comment">//c)如果Service不超时，且在最近运进程列表中</span></span><br><span class="line">            Message msg = mAm.mHandler.obtainMessage(</span><br><span class="line">                    ActivityManagerService.SERVICE_TIMEOUT_MSG);</span><br><span class="line">            msg.obj = proc;</span><br><span class="line">            mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg</span><br><span class="line">                    ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (anrMessage != <span class="keyword">null</span>) &#123;</span><br><span class="line">        mAm.mAppErrors.appNotResponding(proc, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">false</span>, anrMessage);<span class="comment">//ANR的弹框处理</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上述方法会找到当前进程已经超时的Service，经过一些判定后，决定要报告ANR，最终调用AMS.appNotResponding()方法。 走到这一步，ANR机制已经完成了监测报告任务，剩下的任务就是ANR结果的输出，我们称之为ANR的报告机制。 ANR的报告机制是通过AMS.appNotResponding()完成的，Broadcast和InputEvent类型的ANR最终也都会调用这个方法，我们后文再详细展开。</p><p>至此，我们分析了Service的ANR机制：</p><p>通过定时消息跟踪Service的运行，当定时消息被响应时，说明Service还没有运行完成，这就意味着Service ANR。</p><h4 id="Broadcast处理超时"><a href="#Broadcast处理超时" class="headerlink" title="Broadcast处理超时"></a>Broadcast处理超时</h4><p>应用程序可以注册广播接收器，实现BroadcastReceiver.onReceive()方法来完成对广播的处理。通常，这个方法是在主线程执行的，Android限定它执行时间不能超过10秒，否则，就会引发ANR。</p><p>onReceive()也可以调度在其他线程执行，通过Context.registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)这个方法注册广播接收器， 可以指定一个处理的Handler，将onReceive()调度在非主线程执行。</p><p>这里先把问题抛出来了：</p><blockquote><p>Android如何将广播投递给各个应用程序？<br>Android如何检测广播处理超时？</p></blockquote><p>广播消息的调度<br>AMS维护了两个广播队列BroadcastQueue:</p><blockquote><p> foreground queue，前台队列的超时时间是10秒<br> background queue，后台队列的超时时间是60秒</p></blockquote><p>之所以有两个，就是因为要区分的不同超时时间。所有发送的广播都会进入到队列中等待调度，在发送广播时，可以通过Intent.FLAG_RECEIVER_FOREGROUND参数将广播投递到前台队列。 AMS线程会不断地从队列中取出广播消息派发到各个接收器(BroadcastReceiver)。当要派发广播时，AMS会调用BroadcastQueue.scheduleBroadcastsLocked()方法：</p><p><strong> 参见 Android中广播注册和发送机制分析 </strong></p><p>frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scheduleBroadcastsLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, <span class="string">"Schedule broadcasts ["</span></span><br><span class="line">            + mQueueName + <span class="string">"]: current="</span></span><br><span class="line">            + mBroadcastsScheduled);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mBroadcastsScheduled) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, <span class="keyword">this</span>));</span><br><span class="line">    mBroadcastsScheduled = <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述方法中，往AMS线程的消息队列发送BROADCAST_INTENT_MSG消息，由此也可以看到真正派发广播的是AMS线程(system_server进程中的ActivityManager线程)。 由于上述方法可能被并发调用，所以通过mBroadcastsScheduled这个变量来标识BROADCAST_INTENT_MSG是不是已经被AMS线程接收了，当已经抛出的消息还未被接受时，不需要重新抛出。 该消息被接收后的处理逻辑如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> BroadcastHandler mHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">BroadcastHandler</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BroadcastHandler</span><span class="params">(Looper looper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(looper, <span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">            <span class="keyword">case</span> BROADCAST_INTENT_MSG: &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(</span><br><span class="line">                        TAG_BROADCAST, <span class="string">"Received BROADCAST_INTENT_MSG"</span>);</span><br><span class="line">                processNextBroadcast(<span class="keyword">true</span>);</span><br><span class="line">            &#125; <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> BROADCAST_TIMEOUT_MSG: &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (mService) &#123;</span><br><span class="line">                    broadcastTimeoutLocked(<span class="keyword">true</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 直接调用BroadcastQueue.processNextBroadcast()方法，fromMsg参数为true表示这是一次来自BROADCAST_INTENT_MSG消息的派发请求。 BroadcastQueue.processNextBroadcast()是派发广播消息最为核心的函数，代码量自然也不小，我们分成几个部分来分析：<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// processNextBroadcast部分1：处理非串行广播消息</span></span><br><span class="line"><span class="keyword">final</span> ActivityManagerService mService;<span class="comment">//mService是ActivityManagerService</span></span><br><span class="line">   .....</span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">processNextBroadcast</span><span class="params">(<span class="keyword">boolean</span> fromMsg)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">synchronized</span>(mService) &#123;</span><br><span class="line">           BroadcastRecord r;</span><br><span class="line"></span><br><span class="line">           <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, <span class="string">"processNextBroadcast ["</span></span><br><span class="line">                   + mQueueName + <span class="string">"]: "</span></span><br><span class="line">                   + mParallelBroadcasts.size() + <span class="string">" parallel broadcasts, "</span></span><br><span class="line">                   + mOrderedBroadcasts.size() + <span class="string">" ordered broadcasts"</span>);</span><br><span class="line"></span><br><span class="line">           mService.updateCpuStats();<span class="comment">//更新CPU状态信息</span></span><br><span class="line"></span><br><span class="line">           <span class="comment">//1. 更新mBroadcastsScheduled 标志状态</span></span><br><span class="line">           <span class="keyword">if</span> (fromMsg) &#123;</span><br><span class="line">               mBroadcastsScheduled = <span class="keyword">false</span>;</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// First, deliver any non-serialized broadcasts right away.</span></span><br><span class="line">           <span class="comment">//2. 处理并行广播</span></span><br><span class="line">           <span class="keyword">while</span> (mParallelBroadcasts.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">               r = mParallelBroadcasts.remove(<span class="number">0</span>);</span><br><span class="line">               r.dispatchTime = SystemClock.uptimeMillis();</span><br><span class="line">               r.dispatchClockTime = System.currentTimeMillis();</span><br><span class="line"></span><br><span class="line">               <span class="keyword">if</span> (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) &#123;</span><br><span class="line">                   Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,</span><br><span class="line">                       createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),</span><br><span class="line">                       System.identityHashCode(r));</span><br><span class="line">                   Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,</span><br><span class="line">                       createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),</span><br><span class="line">                       System.identityHashCode(r));</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               <span class="keyword">final</span> <span class="keyword">int</span> N = r.receivers.size();</span><br><span class="line">               <span class="keyword">if</span> (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, <span class="string">"Processing parallel broadcast ["</span></span><br><span class="line">                       + mQueueName + <span class="string">"] "</span> + r);</span><br><span class="line">               <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;N; i++) &#123;</span><br><span class="line">                   Object target = r.receivers.get(i);</span><br><span class="line">                   <span class="keyword">if</span> (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,</span><br><span class="line">                           <span class="string">"Delivering non-ordered on ["</span> + mQueueName + <span class="string">"] to registered "</span></span><br><span class="line">                           + target + <span class="string">": "</span> + r);</span><br><span class="line">                   deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, <span class="keyword">false</span>, i);</span><br><span class="line">               &#125;</span><br><span class="line">               addBroadcastToHistoryLocked(r);</span><br><span class="line">               <span class="keyword">if</span> (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, <span class="string">"Done with parallel broadcast ["</span></span><br><span class="line">                       + mQueueName + <span class="string">"] "</span> + r);</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// Now take care of the next serialized one...</span></span><br><span class="line"></span><br><span class="line">           <span class="comment">// If we are waiting for a process to come up to handle the next</span></span><br><span class="line">           <span class="comment">// broadcast, then do nothing at this point.  Just in case, we</span></span><br><span class="line">           <span class="comment">// check that the process we're waiting for still exists.</span></span><br><span class="line">           <span class="comment">//3. 处理阻塞广播</span></span><br><span class="line">           <span class="keyword">if</span> (mPendingBroadcast != <span class="keyword">null</span>) &#123;</span><br><span class="line">               <span class="keyword">if</span> (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,</span><br><span class="line">                       <span class="string">"processNextBroadcast ["</span> + mQueueName + <span class="string">"]: waiting for "</span></span><br><span class="line">                       + mPendingBroadcast.curApp);</span><br><span class="line"></span><br><span class="line">               <span class="keyword">boolean</span> isDead; <span class="comment">// isDead表示当前广播消息的进程的存活状态</span></span><br><span class="line">               <span class="keyword">synchronized</span> (mService.mPidsSelfLocked) &#123;</span><br><span class="line">                   ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);</span><br><span class="line">                   isDead = proc == <span class="keyword">null</span> || proc.crashing;</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="keyword">if</span> (!isDead) &#123;</span><br><span class="line">                   <span class="comment">// It's still alive, so keep waiting// 如果还活着，则返回该函数，继续等待下次派发</span></span><br><span class="line">                   <span class="keyword">return</span>;</span><br><span class="line">               &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                   Slog.w(TAG, <span class="string">"pending app  ["</span></span><br><span class="line">                           + mQueueName + <span class="string">"]"</span> + mPendingBroadcast.curApp</span><br><span class="line">                           + <span class="string">" died before responding to broadcast"</span>);</span><br><span class="line">                   mPendingBroadcast.state = BroadcastRecord.IDLE;</span><br><span class="line">                   mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;</span><br><span class="line">                   mPendingBroadcast = <span class="keyword">null</span>;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="comment">//未完待续</span></span><br></pre></td></tr></table></figure></p><p>第一个部分是处理非”串行广播消息”，有以下几个步骤：</p><ol><li><p>设置mBroadcastsScheduled。该变量在前文说过，是对BROADCAST_INTENT_MSG进行控制。 如果是响应BROADCAST_INTENT_MSG 的派发调用，则将mBroadcastsScheduled设为false， 表示本次 BROADCAST_INTENT_MSG 已经处理完毕，可以继续抛出下一次 BROADCAST_INTENT_MSG消息了</p></li><li><p>处理”并行广播消息”。广播接受器有”动态”和”静态”之分，通过Context.registerReceiver()注册的广播接收器为”动态”的，通过AndroidManifest.xml注册的广播接收器为”静态”的。 广播消息有”并行”和”串行”之分，”并行广播消息”都会派发到”动态”接收器，”串行广播消息”则会根据实际情况派发到两种接收器。 我们先不去探究Android为什么这么设计，只关注这两种广播消息派发的区别。在BroadcastQueue维护着两个队列：</p></li></ol><blockquote><p> mParallelBroadcasts，”并行广播消息”都会进入到此队列中排队。”并行广播消息”可以一次性派发完毕，即在一个循环中将广播派发到所有的”动态”接收器</p></blockquote><blockquote><p> mOrderedBroadcasts，”串行广播消息”都会进入到此队列中排队。”串行广播消息”需要轮侯派发，当一个接收器处理完毕后，会再抛出BROADCAST_INTENT_MSG消息， 再次进入BroadcastQueue.processNextBroadcast()处理下一个</p></blockquote><ol start="3"><li>处理阻塞的广播消息。有时候会存在一个广播消息派发不出去的情况，这个广播消息会保存在mPendingBroadcast变量中。新一轮的派发启动时，会判断接收该消息的进程是否还活着， 如果接收进程还活着，那么就继续等待。否则，就放弃这个广播消息</li></ol><p>接下来是最为复杂的一部分，处理”串行广播消息”，ANR监测机制只在这一类广播消息中才发挥作用，也就是说”并行广播消息”是不会发生ANR的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// processNextBroadcast部分2：从队列中取出“串行广播消息”</span></span><br><span class="line">            <span class="keyword">boolean</span> looped = <span class="keyword">false</span>;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">do</span> &#123;<span class="comment">//处理有序广播</span></span><br><span class="line">                <span class="keyword">if</span> (mOrderedBroadcasts.size() == <span class="number">0</span>) &#123;<span class="comment">//有序广播都处理完了时</span></span><br><span class="line">                    <span class="comment">// No more broadcasts pending, so all done!</span></span><br><span class="line">                    mService.scheduleAppGcsLocked();</span><br><span class="line">                    <span class="keyword">if</span> (looped) &#123;</span><br><span class="line">                        <span class="comment">// If we had finished the last ordered broadcast, then</span></span><br><span class="line">                        <span class="comment">// make sure all processes have correct oom and sched</span></span><br><span class="line">                        <span class="comment">// adjustments.</span></span><br><span class="line">                        mService.updateOomAdjLocked();</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                r = mOrderedBroadcasts.get(<span class="number">0</span>);</span><br><span class="line">                <span class="keyword">boolean</span> forceReceive = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 1. 广播消息的第一个ANR监测机制</span></span><br><span class="line">                <span class="comment">// Ensure that even if something goes awry with the timeout</span></span><br><span class="line">                <span class="comment">// detection, we catch "hung" broadcasts here, discard them,</span></span><br><span class="line">                <span class="comment">// and continue to make progress.</span></span><br><span class="line">                <span class="comment">//</span></span><br><span class="line">                <span class="comment">// This is only done if the system is ready so that PRE_BOOT_COMPLETED</span></span><br><span class="line">                <span class="comment">// receivers don't get executed with timeouts. They're intended for</span></span><br><span class="line">                <span class="comment">// one time heavy lifting after system upgrades and can take</span></span><br><span class="line">                <span class="comment">// significant amounts of time.</span></span><br><span class="line">                <span class="keyword">int</span> numReceivers = (r.receivers != <span class="keyword">null</span>) ? r.receivers.size() : <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">if</span> (mService.mProcessesReady &amp;&amp; r.dispatchTime &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">long</span> now = SystemClock.uptimeMillis();</span><br><span class="line">                    <span class="keyword">if</span> ((numReceivers &gt; <span class="number">0</span>) &amp;&amp;</span><br><span class="line">                            (now &gt; r.dispatchTime + (<span class="number">2</span>*mTimeoutPeriod*numReceivers))) &#123;</span><br><span class="line">                        Slog.w(TAG, <span class="string">"Hung broadcast ["</span></span><br><span class="line">                                + mQueueName + <span class="string">"] discarded after timeout failure:"</span></span><br><span class="line">                                + <span class="string">" now="</span> + now</span><br><span class="line">                                + <span class="string">" dispatchTime="</span> + r.dispatchTime</span><br><span class="line">                                + <span class="string">" startTime="</span> + r.receiverTime</span><br><span class="line">                                + <span class="string">" intent="</span> + r.intent</span><br><span class="line">                                + <span class="string">" numReceivers="</span> + numReceivers</span><br><span class="line">                                + <span class="string">" nextReceiver="</span> + r.nextReceiver</span><br><span class="line">                                + <span class="string">" state="</span> + r.state);</span><br><span class="line">                        broadcastTimeoutLocked(<span class="keyword">false</span>); <span class="comment">// forcibly finish this broadcast</span></span><br><span class="line">                        forceReceive = <span class="keyword">true</span>;</span><br><span class="line">                        r.state = BroadcastRecord.IDLE;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (r.state != BroadcastRecord.IDLE) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,</span><br><span class="line">                            <span class="string">"processNextBroadcast("</span></span><br><span class="line">                            + mQueueName + <span class="string">") called when not idle (state="</span></span><br><span class="line">                            + r.state + <span class="string">")"</span>);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 2. 判断该广播消息是否处理完毕</span></span><br><span class="line">                <span class="keyword">if</span> (r.receivers == <span class="keyword">null</span> || r.nextReceiver &gt;= numReceivers</span><br><span class="line">                        || r.resultAbort || forceReceive) &#123;</span><br><span class="line">                    <span class="comment">// No more receivers for this broadcast!  Send the final</span></span><br><span class="line">                    <span class="comment">// result if requested...</span></span><br><span class="line">                    <span class="keyword">if</span> (r.resultTo != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,</span><br><span class="line">                                    <span class="string">"Finishing broadcast ["</span> + mQueueName + <span class="string">"] "</span></span><br><span class="line">                                    + r.intent.getAction() + <span class="string">" app="</span> + r.callerApp);</span><br><span class="line">                            performReceiveLocked(r.callerApp, r.resultTo,</span><br><span class="line">                                <span class="keyword">new</span> Intent(r.intent), r.resultCode,</span><br><span class="line">                                r.resultData, r.resultExtras, <span class="keyword">false</span>, <span class="keyword">false</span>, r.userId);</span><br><span class="line">                            <span class="comment">// Set this to null so that the reference</span></span><br><span class="line">                            <span class="comment">// (local and remote) isn't kept in the mBroadcastHistory.</span></span><br><span class="line">                            r.resultTo = <span class="keyword">null</span>;</span><br><span class="line">                        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                            r.resultTo = <span class="keyword">null</span>;</span><br><span class="line">                            Slog.w(TAG, <span class="string">"Failure ["</span></span><br><span class="line">                                    + mQueueName + <span class="string">"] sending broadcast result of "</span></span><br><span class="line">                                    + r.intent, e);</span><br><span class="line"></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, <span class="string">"Cancelling BROADCAST_TIMEOUT_MSG"</span>);</span><br><span class="line">                    cancelBroadcastTimeoutLocked();</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,</span><br><span class="line">                            <span class="string">"Finished with ordered broadcast "</span> + r);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// ... and on to the next...</span></span><br><span class="line">                    addBroadcastToHistoryLocked(r);</span><br><span class="line">                    <span class="keyword">if</span> (r.intent.getComponent() == <span class="keyword">null</span> &amp;&amp; r.intent.getPackage() == <span class="keyword">null</span></span><br><span class="line">                            &amp;&amp; (r.intent.getFlags()&amp;Intent.FLAG_RECEIVER_REGISTERED_ONLY) == <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="comment">// This was an implicit broadcast... let's record it for posterity.</span></span><br><span class="line">                        mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,</span><br><span class="line">                                r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);</span><br><span class="line">                    &#125;</span><br><span class="line">                    mOrderedBroadcasts.remove(<span class="number">0</span>);</span><br><span class="line">                    r = <span class="keyword">null</span>;</span><br><span class="line">                    looped = <span class="keyword">true</span>;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">while</span> (r == <span class="keyword">null</span>);</span><br><span class="line">            <span class="comment">//未完待续</span></span><br></pre></td></tr></table></figure><p>这部分是一个do-while循环，每次都从mOrderedBroadcasts队列中取出第一条广播消息进行处理。第一个Broadcast ANR监测机制千呼万唤总算是出现了：</p><p>判定当前时间是否已经超过了r.dispatchTime + 2×mTimeoutPeriod×numReceivers:</p><p>dispatchTime表示这一系列广播消息开始派发的时间。“串行广播消息”是逐个接收器派发的，一个接收器处理完毕后，才开始处理下一个消息派发。 开始派发到第一个接收器的时间就是dispatchTime。dispatchTime需要开始等广播消息派发以后才会设定，也就是说，第一次进入processNextBroadcast()时， dispatchTime=0,并不会进入该条件判断</p><p>mTimeoutPeriod由当前BroadcastQueue的类型决定(forground为10秒，background为60秒)。这个时间在初始化BroadcastQueue的时候就设置好了， 本意是限定每一个Receiver处理广播的时间，这里利用它做了一个超时计算</p><p>假设一个广播消息有2个接受器，mTimeoutPeriod是10秒，当2×10×2=40秒后，该广播消息还未处理完毕，就调用broadcastTimeoutLocked()方法， 这个方法会判断当前是不是发生了ANR，我们后文再分析。</p><p>如果广播消息是否已经处理完毕，则从mOrderedBroadcasts中移除，重新循环，处理下一条;否则，就会跳出循环。</p><p>以上代码块完成的主要任务是从队列中取一条“串行广播消息”，接下来就准备派发了：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// processNextBroadcast部分3：串行广播消息的第二个ANR监测机制</span></span><br><span class="line">            <span class="comment">// Get the next receiver...</span></span><br><span class="line">            <span class="keyword">int</span> recIdx = r.nextReceiver++;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Keep track of when this receiver started, and make sure there</span></span><br><span class="line">            <span class="comment">// is a timeout message pending to kill it if need be.</span></span><br><span class="line">            r.receiverTime = SystemClock.uptimeMillis();</span><br><span class="line">            <span class="keyword">if</span> (recIdx == <span class="number">0</span>) &#123;</span><br><span class="line">                r.dispatchTime = r.receiverTime;</span><br><span class="line">                r.dispatchClockTime = System.currentTimeMillis();</span><br><span class="line">                <span class="keyword">if</span> (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) &#123;</span><br><span class="line">                    Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,</span><br><span class="line">                        createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),</span><br><span class="line">                        System.identityHashCode(r));</span><br><span class="line">                    Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,</span><br><span class="line">                        createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),</span><br><span class="line">                        System.identityHashCode(r));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, <span class="string">"Processing ordered broadcast ["</span></span><br><span class="line">                        + mQueueName + <span class="string">"] "</span> + r);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (! mPendingBroadcastTimeoutMessage) &#123;</span><br><span class="line">                <span class="keyword">long</span> timeoutTime = r.receiverTime + mTimeoutPeriod;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,</span><br><span class="line">                        <span class="string">"Submitting BROADCAST_TIMEOUT_MSG ["</span></span><br><span class="line">                        + mQueueName + <span class="string">"] for "</span> + r + <span class="string">" at "</span> + timeoutTime);</span><br><span class="line">                setBroadcastTimeoutLocked(timeoutTime);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//未完待续</span></span><br></pre></td></tr></table></figure></p><p>取出“串行广播消息”后，一旦要开始派发，第二个ANR检测机制就出现了。mPendingBroadcastTimeoutMessage变量用于标识当前是否有阻塞的超时消息， 如果没有则调用BroadcastQueue.setBroadcastTimeoutLocked()：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">setBroadcastTimeoutLocked</span><span class="params">(<span class="keyword">long</span> timeoutTime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (! mPendingBroadcastTimeoutMessage) &#123;</span><br><span class="line">        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, <span class="keyword">this</span>);</span><br><span class="line">        mHandler.sendMessageAtTime(msg, timeoutTime);</span><br><span class="line">        mPendingBroadcastTimeoutMessage = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>通过设置一个定时消息BROADCAST_TIMEOUT_MSG来跟踪当前广播消息的执行情况，这种超时监测机制跟Service ANR很类似，也是抛到AMS线程的消息队列。 如果所有的接收器都处理完毕了，则会调用cancelBroadcastTimeoutLocked()清除该消息;否则，该消息就会响应，并调用broadcastTimeoutLocked()， 这个方法在第一种ANR监测机制的时候调用过，第二种ANR监测机制也会调用，我们留到后文分析。</p><p>继续分析processNextBroadcast函数<br>设置完定时消息后，就开始派发广播消息了，首先是“动态”接收器：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// processNextBroadcast部分4： 向“动态”接收器派发广播消息</span></span><br><span class="line">            <span class="keyword">final</span> BroadcastOptions brOptions = r.options;</span><br><span class="line">            <span class="keyword">final</span> Object nextReceiver = r.receivers.get(recIdx);</span><br><span class="line">            <span class="comment">// 动态接收器的类型都是BroadcastFilter</span></span><br><span class="line">            <span class="keyword">if</span> (nextReceiver <span class="keyword">instanceof</span> BroadcastFilter) &#123;</span><br><span class="line">                <span class="comment">// Simple case: this is a registered receiver who gets</span></span><br><span class="line">                <span class="comment">// a direct call.</span></span><br><span class="line">                BroadcastFilter filter = (BroadcastFilter)nextReceiver;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,</span><br><span class="line">                        <span class="string">"Delivering ordered ["</span></span><br><span class="line">                        + mQueueName + <span class="string">"] to registered "</span></span><br><span class="line">                        + filter + <span class="string">": "</span> + r);</span><br><span class="line">                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);</span><br><span class="line">                <span class="keyword">if</span> (r.receiver == <span class="keyword">null</span> || !r.ordered) &#123;</span><br><span class="line">                    <span class="comment">// The receiver has already finished, so schedule to</span></span><br><span class="line">                    <span class="comment">// process the next one.</span></span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, <span class="string">"Quick finishing ["</span></span><br><span class="line">                            + mQueueName + <span class="string">"]: ordered="</span></span><br><span class="line">                            + r.ordered + <span class="string">" receiver="</span> + r.receiver);</span><br><span class="line">                    r.state = BroadcastRecord.IDLE;</span><br><span class="line">                    scheduleBroadcastsLocked();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (brOptions != <span class="keyword">null</span> &amp;&amp; brOptions.getTemporaryAppWhitelistDuration() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                        scheduleTempWhitelistLocked(filter.owningUid,</span><br><span class="line">                                brOptions.getTemporaryAppWhitelistDuration(), r);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//未完待续</span></span><br></pre></td></tr></table></figure></p><p>“动态”接收器的载体进程一般是处于运行状态的，所以向这种类型的接收器派发消息相对简单，调用BroadcastQueue.deliverToRegisteredReceiverLocked()完成接下来的工作。 但“静态”接收器是在AndroidManifest.xml中注册的，派发的时候，可能广播接收器的载体进程还没有启动，所以，这种场景会复杂很多。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// processNextBroadcast部分5： 向“静态”接收器派发广播消息</span></span><br><span class="line">            <span class="comment">// Hard case: need to instantiate the receiver, possibly</span></span><br><span class="line">            <span class="comment">// starting its application process to host it.</span></span><br><span class="line">            <span class="comment">//// 静态接收器的类型都是 ResolveInfo</span></span><br><span class="line">            ResolveInfo info =</span><br><span class="line">                (ResolveInfo)nextReceiver;</span><br><span class="line">            ComponentName component = <span class="keyword">new</span> ComponentName(</span><br><span class="line">                    info.activityInfo.applicationInfo.packageName,</span><br><span class="line">                    info.activityInfo.name);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">boolean</span> skip = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">if</span> (brOptions != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">                    (info.activityInfo.applicationInfo.targetSdkVersion</span><br><span class="line">                            &lt; brOptions.getMinManifestReceiverApiLevel() ||</span><br><span class="line">                    info.activityInfo.applicationInfo.targetSdkVersion</span><br><span class="line">                            &gt; brOptions.getMaxManifestReceiverApiLevel())) &#123;</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 1. 权限检查</span></span><br><span class="line">            <span class="keyword">int</span> perm = mService.checkComponentPermission(info.activityInfo.permission,</span><br><span class="line">                    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,</span><br><span class="line">                    info.activityInfo.exported);</span><br><span class="line">            <span class="keyword">if</span> (!skip &amp;&amp; perm != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!info.activityInfo.exported) &#123;</span><br><span class="line">                    Slog.w(TAG, <span class="string">"Permission Denial: broadcasting "</span></span><br><span class="line">                            + r.intent.toString()</span><br><span class="line">                            + <span class="string">" from "</span> + r.callerPackage + <span class="string">" (pid="</span> + r.callingPid</span><br><span class="line">                            + <span class="string">", uid="</span> + r.callingUid + <span class="string">")"</span></span><br><span class="line">                            + <span class="string">" is not exported from uid "</span> + info.activityInfo.applicationInfo.uid</span><br><span class="line">                            + <span class="string">" due to receiver "</span> + component.flattenToShortString());</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    Slog.w(TAG, <span class="string">"Permission Denial: broadcasting "</span></span><br><span class="line">                            + r.intent.toString()</span><br><span class="line">                            + <span class="string">" from "</span> + r.callerPackage + <span class="string">" (pid="</span> + r.callingPid</span><br><span class="line">                            + <span class="string">", uid="</span> + r.callingUid + <span class="string">")"</span></span><br><span class="line">                            + <span class="string">" requires "</span> + info.activityInfo.permission</span><br><span class="line">                            + <span class="string">" due to receiver "</span> + component.flattenToShortString());</span><br><span class="line">                &#125;</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!skip &amp;&amp; info.activityInfo.permission != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);</span><br><span class="line">                <span class="keyword">if</span> (opCode != AppOpsManager.OP_NONE</span><br><span class="line">                        &amp;&amp; mService.mAppOpsService.noteOperation(opCode, r.callingUid,</span><br><span class="line">                                r.callerPackage) != AppOpsManager.MODE_ALLOWED) &#123;</span><br><span class="line">                    Slog.w(TAG, <span class="string">"Appop Denial: broadcasting "</span></span><br><span class="line">                            + r.intent.toString()</span><br><span class="line">                            + <span class="string">" from "</span> + r.callerPackage + <span class="string">" (pid="</span></span><br><span class="line">                            + r.callingPid + <span class="string">", uid="</span> + r.callingUid + <span class="string">")"</span></span><br><span class="line">                            + <span class="string">" requires appop "</span> + AppOpsManager.permissionToOp(</span><br><span class="line">                                    info.activityInfo.permission)</span><br><span class="line">                            + <span class="string">" due to registered receiver "</span></span><br><span class="line">                            + component.flattenToShortString());</span><br><span class="line">                    skip = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip &amp;&amp; info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &amp;&amp;</span><br><span class="line">                r.requiredPermissions != <span class="keyword">null</span> &amp;&amp; r.requiredPermissions.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; r.requiredPermissions.length; i++) &#123;</span><br><span class="line">                    String requiredPermission = r.requiredPermissions[i];</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        perm = AppGlobals.getPackageManager().</span><br><span class="line">                                checkPermission(requiredPermission,</span><br><span class="line">                                        info.activityInfo.applicationInfo.packageName,</span><br><span class="line">                                        UserHandle</span><br><span class="line">                                                .getUserId(info.activityInfo.applicationInfo.uid));</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                        perm = PackageManager.PERMISSION_DENIED;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (perm != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">                        Slog.w(TAG, <span class="string">"Permission Denial: receiving "</span></span><br><span class="line">                                + r.intent + <span class="string">" to "</span></span><br><span class="line">                                + component.flattenToShortString()</span><br><span class="line">                                + <span class="string">" requires "</span> + requiredPermission</span><br><span class="line">                                + <span class="string">" due to sender "</span> + r.callerPackage</span><br><span class="line">                                + <span class="string">" (uid "</span> + r.callingUid + <span class="string">")"</span>);</span><br><span class="line">                        skip = <span class="keyword">true</span>;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">int</span> appOp = AppOpsManager.permissionToOpCode(requiredPermission);</span><br><span class="line">                    <span class="keyword">if</span> (appOp != AppOpsManager.OP_NONE &amp;&amp; appOp != r.appOp</span><br><span class="line">                            &amp;&amp; mService.mAppOpsService.noteOperation(appOp,</span><br><span class="line">                            info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)</span><br><span class="line">                            != AppOpsManager.MODE_ALLOWED) &#123;</span><br><span class="line">                        Slog.w(TAG, <span class="string">"Appop Denial: receiving "</span></span><br><span class="line">                                + r.intent + <span class="string">" to "</span></span><br><span class="line">                                + component.flattenToShortString()</span><br><span class="line">                                + <span class="string">" requires appop "</span> + AppOpsManager.permissionToOp(</span><br><span class="line">                                requiredPermission)</span><br><span class="line">                                + <span class="string">" due to sender "</span> + r.callerPackage</span><br><span class="line">                                + <span class="string">" (uid "</span> + r.callingUid + <span class="string">")"</span>);</span><br><span class="line">                        skip = <span class="keyword">true</span>;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip &amp;&amp; r.appOp != AppOpsManager.OP_NONE</span><br><span class="line">                    &amp;&amp; mService.mAppOpsService.noteOperation(r.appOp,</span><br><span class="line">                    info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)</span><br><span class="line">                    != AppOpsManager.MODE_ALLOWED) &#123;</span><br><span class="line">                Slog.w(TAG, <span class="string">"Appop Denial: receiving "</span></span><br><span class="line">                        + r.intent + <span class="string">" to "</span></span><br><span class="line">                        + component.flattenToShortString()</span><br><span class="line">                        + <span class="string">" requires appop "</span> + AppOpsManager.opToName(r.appOp)</span><br><span class="line">                        + <span class="string">" due to sender "</span> + r.callerPackage</span><br><span class="line">                        + <span class="string">" (uid "</span> + r.callingUid + <span class="string">")"</span>);</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip) &#123;</span><br><span class="line">                skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,</span><br><span class="line">                        r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">boolean</span> isSingleton = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                isSingleton = mService.isSingleton(info.activityInfo.processName,</span><br><span class="line">                        info.activityInfo.applicationInfo,</span><br><span class="line">                        info.activityInfo.name, info.activityInfo.flags);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SecurityException e) &#123;</span><br><span class="line">                Slog.w(TAG, e.getMessage());</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> ((info.activityInfo.flags&amp;ActivityInfo.FLAG_SINGLE_USER) != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (ActivityManager.checkUidPermission(</span><br><span class="line">                        android.Manifest.permission.INTERACT_ACROSS_USERS,</span><br><span class="line">                        info.activityInfo.applicationInfo.uid)</span><br><span class="line">                                != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">                    Slog.w(TAG, <span class="string">"Permission Denial: Receiver "</span> + component.flattenToShortString()</span><br><span class="line">                            + <span class="string">" requests FLAG_SINGLE_USER, but app does not hold "</span></span><br><span class="line">                            + android.Manifest.permission.INTERACT_ACROSS_USERS);</span><br><span class="line">                    skip = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip &amp;&amp; info.activityInfo.applicationInfo.isInstantApp()</span><br><span class="line">                    &amp;&amp; r.callingUid != info.activityInfo.applicationInfo.uid) &#123;</span><br><span class="line">                Slog.w(TAG, <span class="string">"Instant App Denial: receiving "</span></span><br><span class="line">                        + r.intent</span><br><span class="line">                        + <span class="string">" to "</span> + component.flattenToShortString()</span><br><span class="line">                        + <span class="string">" due to sender "</span> + r.callerPackage</span><br><span class="line">                        + <span class="string">" (uid "</span> + r.callingUid + <span class="string">")"</span></span><br><span class="line">                        + <span class="string">" Instant Apps do not support manifest receivers"</span>);</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip &amp;&amp; r.callerInstantApp</span><br><span class="line">                    &amp;&amp; (info.activityInfo.flags &amp; ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == <span class="number">0</span></span><br><span class="line">                    &amp;&amp; r.callingUid != info.activityInfo.applicationInfo.uid) &#123;</span><br><span class="line">                Slog.w(TAG, <span class="string">"Instant App Denial: receiving "</span></span><br><span class="line">                        + r.intent</span><br><span class="line">                        + <span class="string">" to "</span> + component.flattenToShortString()</span><br><span class="line">                        + <span class="string">" requires receiver have visibleToInstantApps set"</span></span><br><span class="line">                        + <span class="string">" due to sender "</span> + r.callerPackage</span><br><span class="line">                        + <span class="string">" (uid "</span> + r.callingUid + <span class="string">")"</span>);</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip) &#123;</span><br><span class="line">                r.manifestCount++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                r.manifestSkipCount++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (r.curApp != <span class="keyword">null</span> &amp;&amp; r.curApp.crashing) &#123;</span><br><span class="line">                <span class="comment">// If the target process is crashing, just skip it.</span></span><br><span class="line">                Slog.w(TAG, <span class="string">"Skipping deliver ordered ["</span> + mQueueName + <span class="string">"] "</span> + r</span><br><span class="line">                        + <span class="string">" to "</span> + r.curApp + <span class="string">": process crashing"</span>);</span><br><span class="line">                skip = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!skip) &#123;</span><br><span class="line">                <span class="keyword">boolean</span> isAvailable = <span class="keyword">false</span>;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    isAvailable = AppGlobals.getPackageManager().isPackageAvailable(</span><br><span class="line">                            info.activityInfo.packageName,</span><br><span class="line">                            UserHandle.getUserId(info.activityInfo.applicationInfo.uid));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    <span class="comment">// all such failures mean we skip this receiver</span></span><br><span class="line">                    Slog.w(TAG, <span class="string">"Exception getting recipient info for "</span></span><br><span class="line">                            + info.activityInfo.packageName, e);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (!isAvailable) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,</span><br><span class="line">                            <span class="string">"Skipping delivery to "</span> + info.activityInfo.packageName + <span class="string">" / "</span></span><br><span class="line">                            + info.activityInfo.applicationInfo.uid</span><br><span class="line">                            + <span class="string">" : package no longer available"</span>);</span><br><span class="line">                    skip = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If permissions need a review before any of the app components can run, we drop</span></span><br><span class="line">            <span class="comment">// the broadcast and if the calling app is in the foreground and the broadcast is</span></span><br><span class="line">            <span class="comment">// explicit we launch the review UI passing it a pending intent to send the skipped</span></span><br><span class="line">            <span class="comment">// broadcast.</span></span><br><span class="line">            <span class="keyword">if</span> (mService.mPermissionReviewRequired &amp;&amp; !skip) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!requestStartTargetPermissionsReviewIfNeededLocked(r,</span><br><span class="line">                        info.activityInfo.packageName, UserHandle.getUserId(</span><br><span class="line">                                info.activityInfo.applicationInfo.uid))) &#123;</span><br><span class="line">                    skip = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// This is safe to do even if we are skipping the broadcast, and we need</span></span><br><span class="line">            <span class="comment">// this information now to evaluate whether it is going to be allowed to run.</span></span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">int</span> receiverUid = info.activityInfo.applicationInfo.uid;</span><br><span class="line">            <span class="comment">// If it's a singleton, it needs to be the same app or a special app</span></span><br><span class="line">            <span class="keyword">if</span> (r.callingUid != Process.SYSTEM_UID &amp;&amp; isSingleton</span><br><span class="line">                    &amp;&amp; mService.isValidSingletonCall(r.callingUid, receiverUid)) &#123;</span><br><span class="line">                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, <span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 2. 获取接收器所在的进程</span></span><br><span class="line">            String targetProcess = info.activityInfo.processName;</span><br><span class="line">            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,</span><br><span class="line">                    info.activityInfo.applicationInfo.uid, <span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!skip) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> allowed = mService.getAppStartModeLocked(</span><br><span class="line">                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,</span><br><span class="line">                        info.activityInfo.applicationInfo.targetSdkVersion, -<span class="number">1</span>, <span class="keyword">true</span>, <span class="keyword">false</span>);</span><br><span class="line">                <span class="keyword">if</span> (allowed != ActivityManager.APP_START_MODE_NORMAL) &#123;</span><br><span class="line">                    <span class="comment">// We won't allow this receiver to be launched if the app has been</span></span><br><span class="line">                    <span class="comment">// completely disabled from launches, or it was not explicitly sent</span></span><br><span class="line">                    <span class="comment">// to it and the app is in a state that should not receive it</span></span><br><span class="line">                    <span class="comment">// (depending on how getAppStartModeLocked has determined that).</span></span><br><span class="line">                    <span class="keyword">if</span> (allowed == ActivityManager.APP_START_MODE_DISABLED) &#123;</span><br><span class="line">                        Slog.w(TAG, <span class="string">"Background execution disabled: receiving "</span></span><br><span class="line">                                + r.intent + <span class="string">" to "</span></span><br><span class="line">                                + component.flattenToShortString());</span><br><span class="line">                        skip = <span class="keyword">true</span>;</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (((r.intent.getFlags()&amp;Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != <span class="number">0</span>)</span><br><span class="line">                            || (r.intent.getComponent() == <span class="keyword">null</span></span><br><span class="line">                                &amp;&amp; r.intent.getPackage() == <span class="keyword">null</span></span><br><span class="line">                                &amp;&amp; ((r.intent.getFlags()</span><br><span class="line">                                        &amp; Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == <span class="number">0</span>)</span><br><span class="line">                                &amp;&amp; !isSignaturePerm(r.requiredPermissions))) &#123;</span><br><span class="line">                        mService.addBackgroundCheckViolationLocked(r.intent.getAction(),</span><br><span class="line">                                component.getPackageName());</span><br><span class="line">                        Slog.w(TAG, <span class="string">"Background execution not allowed: receiving "</span></span><br><span class="line">                                + r.intent + <span class="string">" to "</span></span><br><span class="line">                                + component.flattenToShortString());</span><br><span class="line">                        skip = <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!skip) &#123;</span><br><span class="line">                skip = !mService.isAutoStartAllowed(info.activityInfo.applicationInfo.uid, info.activityInfo.applicationInfo.packageName);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (skip) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,</span><br><span class="line">                        <span class="string">"Skipping delivery of ordered ["</span> + mQueueName + <span class="string">"] "</span></span><br><span class="line">                        + r + <span class="string">" for whatever reason"</span>);</span><br><span class="line">                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;</span><br><span class="line">                r.receiver = <span class="keyword">null</span>;</span><br><span class="line">                r.curFilter = <span class="keyword">null</span>;</span><br><span class="line">                r.state = BroadcastRecord.IDLE;</span><br><span class="line">                scheduleBroadcastsLocked();</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;</span><br><span class="line">            r.state = BroadcastRecord.APP_RECEIVE;</span><br><span class="line">            r.curComponent = component;</span><br><span class="line">            r.curReceiver = info.activityInfo;</span><br><span class="line">            <span class="keyword">if</span> (DEBUG_MU &amp;&amp; r.callingUid &gt; UserHandle.PER_USER_RANGE) &#123;</span><br><span class="line">                Slog.v(TAG_MU, <span class="string">"Updated broadcast record activity info for secondary user, "</span></span><br><span class="line">                        + info.activityInfo + <span class="string">", callingUid = "</span> + r.callingUid + <span class="string">", uid = "</span></span><br><span class="line">                        + info.activityInfo.applicationInfo.uid);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (brOptions != <span class="keyword">null</span> &amp;&amp; brOptions.getTemporaryAppWhitelistDuration() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                scheduleTempWhitelistLocked(receiverUid,</span><br><span class="line">                        brOptions.getTemporaryAppWhitelistDuration(), r);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Broadcast is being executed, its package can't be stopped.</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                AppGlobals.getPackageManager().setPackageStoppedState(</span><br><span class="line">                        r.curComponent.getPackageName(), <span class="keyword">false</span>, UserHandle.getUserId(r.callingUid));</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IllegalArgumentException e) &#123;</span><br><span class="line">                Slog.w(TAG, <span class="string">"Failed trying to unstop package "</span></span><br><span class="line">                        + r.curComponent.getPackageName() + <span class="string">": "</span> + e);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 3. 进程已经启动</span></span><br><span class="line">            <span class="comment">// Is this receiver's application already running?</span></span><br><span class="line">            <span class="keyword">if</span> (app != <span class="keyword">null</span> &amp;&amp; app.thread != <span class="keyword">null</span> &amp;&amp; !app.killed) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    app.addPackage(info.activityInfo.packageName,</span><br><span class="line">                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);</span><br><span class="line">                    processCurBroadcastLocked(r, app);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                    Slog.w(TAG, <span class="string">"Exception when sending broadcast to "</span></span><br><span class="line">                          + r.curComponent, e);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (RuntimeException e) &#123;</span><br><span class="line">                    Slog.wtf(TAG, <span class="string">"Failed sending broadcast to "</span></span><br><span class="line">                            + r.curComponent + <span class="string">" with "</span> + r.intent, e);</span><br><span class="line">                    <span class="comment">// If some unexpected exception happened, just skip</span></span><br><span class="line">                    <span class="comment">// this broadcast.  At this point we are not in the call</span></span><br><span class="line">                    <span class="comment">// from a client, so throwing an exception out from here</span></span><br><span class="line">                    <span class="comment">// will crash the entire system instead of just whoever</span></span><br><span class="line">                    <span class="comment">// sent the broadcast.</span></span><br><span class="line">                    logBroadcastReceiverDiscardLocked(r);</span><br><span class="line">                    finishReceiverLocked(r, r.resultCode, r.resultData,</span><br><span class="line">                            r.resultExtras, r.resultAbort, <span class="keyword">false</span>);</span><br><span class="line">                    scheduleBroadcastsLocked();</span><br><span class="line">                    <span class="comment">// We need to reset the state if we failed to start the receiver.</span></span><br><span class="line">                    r.state = BroadcastRecord.IDLE;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// If a dead object exception was thrown -- fall through to</span></span><br><span class="line">                <span class="comment">// restart the application.</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 4. 进程还未启动</span></span><br><span class="line">            <span class="comment">// Not running -- get it started, to be executed when the app comes up.</span></span><br><span class="line">            <span class="keyword">if</span> (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,</span><br><span class="line">                    <span class="string">"Need to start app ["</span></span><br><span class="line">                    + mQueueName + <span class="string">"] "</span> + targetProcess + <span class="string">" for broadcast "</span> + r);</span><br><span class="line">            <span class="keyword">if</span> ((r.curApp=mService.startProcessLocked(targetProcess,</span><br><span class="line">                    info.activityInfo.applicationInfo, <span class="keyword">true</span>,</span><br><span class="line">                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,</span><br><span class="line">                    <span class="string">"broadcast"</span>, r.curComponent,</span><br><span class="line">                    (r.intent.getFlags()&amp;Intent.FLAG_RECEIVER_BOOT_UPGRADE) != <span class="number">0</span>, <span class="keyword">false</span>, <span class="keyword">false</span>))</span><br><span class="line">                            == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">// Ah, this recipient is unavailable.  Finish it if necessary,</span></span><br><span class="line">                <span class="comment">// and mark the broadcast record as ready for the next.</span></span><br><span class="line">                Slog.w(TAG, <span class="string">"Unable to launch app "</span></span><br><span class="line">                        + info.activityInfo.applicationInfo.packageName + <span class="string">"/"</span></span><br><span class="line">                        + info.activityInfo.applicationInfo.uid + <span class="string">" for broadcast "</span></span><br><span class="line">                        + r.intent + <span class="string">": process is bad"</span>);</span><br><span class="line">                logBroadcastReceiverDiscardLocked(r);</span><br><span class="line">                finishReceiverLocked(r, r.resultCode, r.resultData,</span><br><span class="line">                        r.resultExtras, r.resultAbort, <span class="keyword">false</span>);</span><br><span class="line">                scheduleBroadcastsLocked();</span><br><span class="line">                r.state = BroadcastRecord.IDLE;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 5. 进程启动失败</span></span><br><span class="line">            mPendingBroadcast = r;</span><br><span class="line">            mPendingBroadcastRecvIndex = recIdx;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// processNextBroadcast完</span></span><br></pre></td></tr></table></figure><p>• 1.“静态”接收器是ResolveInfo，需要通过PackageManager获取包信息，进行权限检查。权限检查的内容非常庞大，此处不表。</p><p>• 2.经过一系列复杂的权限检查后，终于可以向目标接收器派发了。通过AMS.getProcessRecordLocked()获取广播接收器的进程信息</p><p>• 3.如果app.thread ！= null，则进程已经启动，就可以调用BroadcastQueue.processCurBroadcastLocked()进行接下来的派发处理了</p><p>• 4.如果进程还没有启动，则需要通过AMS.startProcessLocked()来启动进程，当前消息并未派发，调用BroadcastQueue.scheduleBroadcastsLocked()进入下一次的调度</p><p>• 5.如果进程启动失败了，则当前消息记录成mPendingBroadcast，即阻塞的广播消息，等待下一次调度时处理</p><p>庞大的processNextBroadcast()终于完结了，它的功能就是对广播消息进行调度，该方法被设计得十分复杂而精巧，用于应对不同的广播消息和接收器的处理。</p><p>广播消息的跨进程传递调度是完成了，接下来，我们就来分析被调度广播消息如何到达应用程序。上文的分析中，最终有两个方法将广播消息派发出去： BroadcastQueue.deliverToRegisteredReceiverLocked() 和BroadcastQueue.processCurBroadcastLocked()。</p><p>我们先不展开这两个函数的逻辑，试想要将广播消息的从AMS线程所在的system_server进程传递到应用程序的进程，该怎么实现？ 自然需要用到跨进程调用，Android中最常规的手段就是Binder机制。没错，广播消息派发到应用进程就是这么玩的。</p><p>对于应用程序已经启动(app.thread != null)的情况，会通过IApplicationThread发起跨进程调用， 调用关系如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ActivityThread.ApplicationThread.scheduleReceiver()</span><br><span class="line">└── ActivityThread.handleReceiver()</span><br><span class="line">    └── BroadcastReceiver.onReceive()</span><br></pre></td></tr></table></figure></p><pre><code>对于应用程序还未启动的情况，会调用IIntentReceiver发起跨进程调用，应用进程的实现在LoadedApk.ReceiverDispatcher.IntentReceiver 中， 调用关系如下：</code></pre><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">LoadedApk.ReceiverDispatcher.IntentReceiver.performReceive()</span><br><span class="line">└── LoadedApk.ReceiverDispatcher.performReceiver()</span><br><span class="line">    └── LoadedApk.ReceiverDispatcher.Args.run()</span><br><span class="line">        └── BroadcastReceiver.onReceive()</span><br></pre></td></tr></table></figure><p>最终，都会调用到BroadcastReceiver.onReceive()，在应用进程执行接收广播消息的具体动作。 对于“串行广播消息”而言，执行完了以后，还需要通知system_server进程，才能继续将广播消息派发到下一个接收器，这又需要跨进程调用了。 应用进程在处理完广播消息后，即在BroadcastReceiver.onReceive()执行完毕后，会调用BroadcastReceiver.PendingResult.finish()， 接下来的调用关系如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">BroadcastReceiver.PendingResult.finish()</span><br><span class="line">└── BroadcastReceiver.PendingResult.sendFinished()</span><br><span class="line">    └── IActivityManager.finishReceiver()</span><br><span class="line">        └── ActivityManagerService.finishReceiver()</span><br><span class="line">            └── BroadcastQueue.processNextBroadcat()</span><br></pre></td></tr></table></figure></p><p>通过IActivityManager发起了一个从应用进程到system_server进程的调用，最终在AMS线程中，又走到了BroadcastQueue.processNextBroadcat(), 开始下一轮的调度。</p><p>broadcastTimeoutLocked()方法<br>前文说过，两种ANR机制最终都会调用BroadcastQueue.broadcastTimeoutLocked()方法， 第一种ANR监测生效时，会将fromMsg设置为false;第二种ANR监测生效时，会将fromMsg参数为True时，表示当前正在响应BROADCAST_TIMEOUT_MSG消息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">broadcastTimeoutLocked</span><span class="params">(<span class="keyword">boolean</span> fromMsg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (fromMsg) &#123;<span class="comment">// 1. 设置mPendingBroadcastTimeoutMessage</span></span><br><span class="line">            mPendingBroadcastTimeoutMessage = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mOrderedBroadcasts.size() == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2. 判断第二种ANR机制是否超时</span></span><br><span class="line">        <span class="keyword">long</span> now = SystemClock.uptimeMillis();</span><br><span class="line">        BroadcastRecord r = mOrderedBroadcasts.get(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (fromMsg) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!mService.mProcessesReady) &#123;</span><br><span class="line">                <span class="comment">// Only process broadcast timeouts if the system is ready. That way</span></span><br><span class="line">                <span class="comment">// PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended</span></span><br><span class="line">                <span class="comment">// to do heavy lifting for system up.</span></span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">long</span> timeoutTime = r.receiverTime + mTimeoutPeriod;</span><br><span class="line">            <span class="keyword">if</span> (timeoutTime &gt; now) &#123;</span><br><span class="line">                <span class="comment">// We can observe premature timeouts because we do not cancel and reset the</span></span><br><span class="line">                <span class="comment">// broadcast timeout message after each receiver finishes.  Instead, we set up</span></span><br><span class="line">                <span class="comment">// an initial timeout then kick it down the road a little further as needed</span></span><br><span class="line">                <span class="comment">// when it expires.</span></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,</span><br><span class="line">                        <span class="string">"Premature timeout ["</span></span><br><span class="line">                        + mQueueName + <span class="string">"] @ "</span> + now + <span class="string">": resetting BROADCAST_TIMEOUT_MSG for "</span></span><br><span class="line">                        + timeoutTime);</span><br><span class="line">                setBroadcastTimeoutLocked(timeoutTime);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        BroadcastRecord br = mOrderedBroadcasts.get(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (br.state == BroadcastRecord.WAITING_SERVICES) &#123;</span><br><span class="line">            <span class="comment">// In this case the broadcast had already finished, but we had decided to wait</span></span><br><span class="line">            <span class="comment">// for started services to finish as well before going on.  So if we have actually</span></span><br><span class="line">            <span class="comment">// waited long enough time timeout the broadcast, let's give up on the whole thing</span></span><br><span class="line">            <span class="comment">// and just move on to the next.</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"Waited long enough for: "</span> + (br.curComponent != <span class="keyword">null</span></span><br><span class="line">                    ? br.curComponent.flattenToShortString() : <span class="string">"(null)"</span>));</span><br><span class="line">            br.curComponent = <span class="keyword">null</span>;</span><br><span class="line">            br.state = BroadcastRecord.IDLE;</span><br><span class="line">            processNextBroadcast(<span class="keyword">false</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Slog.w(TAG, <span class="string">"Timeout of broadcast "</span> + r + <span class="string">" - receiver="</span> + r. receiver</span><br><span class="line">                + <span class="string">", started "</span> + (now - r.receiverTime) + <span class="string">"ms ago"</span>);</span><br><span class="line">        r.receiverTime = now;</span><br><span class="line">        r.anrCount++;</span><br><span class="line"></span><br><span class="line">        ProcessRecord app = <span class="keyword">null</span>;</span><br><span class="line">        String anrMessage = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        Object curReceiver;</span><br><span class="line">        <span class="keyword">if</span> (r.nextReceiver &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            curReceiver = r.receivers.get(r.nextReceiver-<span class="number">1</span>);</span><br><span class="line">            r.delivery[r.nextReceiver-<span class="number">1</span>] = BroadcastRecord.DELIVERY_TIMEOUT;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            curReceiver = r.curReceiver;</span><br><span class="line">        &#125;</span><br><span class="line">        Slog.w(TAG, <span class="string">"Receiver during timeout of "</span> + r + <span class="string">" : "</span> + curReceiver);</span><br><span class="line">        logBroadcastReceiverDiscardLocked(r);</span><br><span class="line">        <span class="keyword">if</span> (curReceiver != <span class="keyword">null</span> &amp;&amp; curReceiver <span class="keyword">instanceof</span> BroadcastFilter) &#123;</span><br><span class="line">            BroadcastFilter bf = (BroadcastFilter)curReceiver;</span><br><span class="line">            <span class="keyword">if</span> (bf.receiverList.pid != <span class="number">0</span></span><br><span class="line">                    &amp;&amp; bf.receiverList.pid != ActivityManagerService.MY_PID) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (mService.mPidsSelfLocked) &#123;</span><br><span class="line">                    app = mService.mPidsSelfLocked.get(</span><br><span class="line">                            bf.receiverList.pid);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            app = r.curApp;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (app != <span class="keyword">null</span>) &#123;</span><br><span class="line">            anrMessage = <span class="string">"Broadcast of "</span> + r.intent.toString();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mPendingBroadcast == r) &#123;</span><br><span class="line">            mPendingBroadcast = <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3. 已经超时，则结束对当前接收器，开始新一轮调度</span></span><br><span class="line">        <span class="comment">// Move on to the next receiver.</span></span><br><span class="line">        finishReceiverLocked(r, r.resultCode, r.resultData,</span><br><span class="line">                r.resultExtras, r.resultAbort, <span class="keyword">false</span>);</span><br><span class="line">        scheduleBroadcastsLocked();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 4. 抛出绘制ANR对话框的消息</span></span><br><span class="line">        <span class="keyword">if</span> (anrMessage != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// Post the ANR to the handler since we do not want to process ANRs while</span></span><br><span class="line">            <span class="comment">// potentially holding our lock.</span></span><br><span class="line">            mHandler.post(<span class="keyword">new</span> AppNotResponding(app, anrMessage));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>•  1.mPendingBroadcastTimeoutMessage 标识是否存在未处理的BROADCAST_TIMEOUT_MSG 消息， 将其设置成false，允许继续抛出BROADCAST_TIMEOUT_MSG 消息</p><p>•  2.每次将广播派发到接收器，都会将r.receiverTime 更新，如果判断当前还未超时，则又抛出一个 BROADCAST_TIMEOUT_MSG 息。正常情况下，所有接收器处理完毕后，才会清除 BROADCAST_TIMEOUT_MSG ;否则，每进行一次广播消息的调度，都会抛出 BROADCAST_TIMEOUT_MSG 消息</p><p>•  3.判断已经超时了，说明当前的广播接收器还未处理完毕，则结束掉当前的接收器，开始新一轮广播调度</p><p>•  4.最终，发出绘制ANR对话框的消息</p><p>至此，我们回答了前文提出的两个问题:</p><p>AMS维护着广播队列BroadcastQueue，AMS线程不断从队列中取出消息进行调度，完成广播消息的派发。 在派发“串行广播消息”时，会抛出一个定时消息 BROADCAST_TIMEOUT_MSG ，在广播接收器处理完毕后，AMS会将定时消息清除。 如果BROADCAST_TIMEOUT_MSG得到了响应，就会判断是否广播消息处理超时，最终通知ANR的发生。</p><h4 id="Input处理超时"><a href="#Input处理超时" class="headerlink" title="Input处理超时"></a>Input处理超时</h4><p>应用程序可以接收输入事件(按键、触屏、轨迹球等)，当5秒内没有处理完毕时，则会引发ANR。</p><p>如果Broadcast ANR一样，我们抛出Input ANR的几个问题：</p><blockquote><ol><li>输入事件经历了一些什么工序才能被派发到应用的界面？</li><li>如何检测到输入时间处理超时</li></ol></blockquote><p>输入事件最开始由硬件设备(譬如按键或触摸屏幕)发起，Android有一套输入子系统来发现各种输入事件， 这些事件最终都会被<a href="https://android.googlesource.com/platform/frameworks/native/+/master/services/inputflinger/InputDispatcher.cpp" target="_blank" rel="noopener">InputDispatcher</a>分发到各个需要接收事件的窗口。 那么，窗口如何告之InputDispatcher自己需要处理输入事件呢？Android通过InputChannel 连接InputDispatcher和窗口，InputChannel其实是封装后的Linux管道(Pipe)。 每一个窗口都会有一个独立的InputChannel，窗口需要将这个InputChannel注册到InputDispatcher中:</p><blockquote><p>frameworks/native/services/inputflinger/InputDispatcher.cpp</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">status_t InputDispatcher::registerInputChannel(<span class="keyword">const</span> sp&lt;InputChannel&gt;&amp; inputChannel,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle, bool monitor) &#123;</span><br><span class="line">#if DEBUG_REGISTRATION</span><br><span class="line">    ALOGD(<span class="string">"channel '%s' ~ registerInputChannel - monitor=%s"</span>, inputChannel-&gt;getName().string(),</span><br><span class="line">            toString(monitor));</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">    &#123; <span class="comment">// acquire lock</span></span><br><span class="line">        <span class="function">AutoMutex <span class="title">_l</span><span class="params">(mLock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (getConnectionIndexLocked(inputChannel) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            ALOGW(<span class="string">"Attempted to register already registered input channel '%s'"</span>,</span><br><span class="line">                    inputChannel-&gt;getName().string());</span><br><span class="line">            <span class="keyword">return</span> BAD_VALUE;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        sp&lt;Connection&gt; connection = <span class="keyword">new</span> Connection(inputChannel, inputWindowHandle, monitor);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> fd = inputChannel-&gt;getFd();</span><br><span class="line">        mConnectionsByFd.add(fd, connection);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (monitor) &#123;</span><br><span class="line">            mMonitoringChannels.push(inputChannel);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        mLooper-&gt;addFd(fd, <span class="number">0</span>, ALOOPER_EVENT_INPUT, handleReceiveCallback, <span class="keyword">this</span>);</span><br><span class="line">    &#125; <span class="comment">// release lock</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// Wake the looper because some connections have changed.</span></span><br><span class="line">    mLooper-&gt;wake();</span><br><span class="line">    <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于InputDispatcher而言，每注册一个InputChannel都被视为一个Connection，通过文件描述符来区别。InputDispatcher是一个消息处理循环，当有新的Connection时，就需要唤醒消息循环队列进行处理。</p><p>输入事件的类型有很多，按键、轨迹球、触屏等，Android对这些事件进行了分类，处理这些事件的窗口也被赋予了一个类型 (targetType)：Foucused或Touched ，如果当前输入事件是按键类型，则寻找 Focused类型的窗口;如果当前输入事件类型是触摸类型，则寻找Touched类型的窗口。 InputDispatcher需要经过以下复杂的调用关系，才能把一个输入事件派发出去(调用关系以按键事件为例，触屏事件的调用关系类似)：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">InputDispatcherThread::threadLoop()</span><br><span class="line">└── InputDispatcher::dispatchOnce()</span><br><span class="line">    └── InputDispatcher::dispatchOnceInnerLocked()</span><br><span class="line">        └── InputDispatcher::dispatchKeyLocked()</span><br><span class="line">            └── InputDispatcher::dispatchEventLocked()</span><br><span class="line">                └── InputDispatcher::prepareDispatchCycleLocked()</span><br><span class="line">                    └── InputDispatcher::enqueueDispatchEntriesLocked()</span><br><span class="line">                        └── InputDispatcher::startDispatchCycleLocked()</span><br><span class="line">                            └── InputPublisher::publishKeyEvent()</span><br></pre></td></tr></table></figure><p>具体每个函数的实现逻辑此处不表。我们提炼出几个关键点：</p><blockquote><ol><li>InputDispatcherThread是一个线程，它处理一次消息的派发</li><li>输入事件作为一个消息，需要排队等待派发，每一个Connection都维护两个队列：<br>outboundQueue: 等待发送给窗口的事件。每一个新消息到来，都会先进入到此队列<br>waitQueue: 已经发送给窗口的事件</li><li>publishKeyEvent完成后，表示事件已经派发了，就将事件从outboundQueue挪到了waitQueue</li></ol></blockquote><p>事件经过这么一轮处理，就算是从InputDispatcher派发出去了，但事件是不是被窗口收到了，还需要等待接收方的“finished”通知。 在向InputDispatcher 注册InputChannel 的时候，同时会注册一个回调函数handleReceiveCallback():</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> InputDispatcher::handleReceiveCallback(<span class="keyword">int</span> fd, <span class="keyword">int</span> events, <span class="keyword">void</span>* data) &#123;</span><br><span class="line">    InputDispatcher* d = static_cast&lt;InputDispatcher*&gt;(data);</span><br><span class="line"></span><br><span class="line">    &#123; <span class="comment">// acquire lock</span></span><br><span class="line">        <span class="function">AutoMutex <span class="title">_l</span><span class="params">(d-&gt;mLock)</span></span>;</span><br><span class="line"></span><br><span class="line">        ssize_t connectionIndex = d-&gt;mConnectionsByFd.indexOfKey(fd);</span><br><span class="line">        <span class="keyword">if</span> (connectionIndex &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            ALOGE(<span class="string">"Received spurious receive callback for unknown input channel.  "</span></span><br><span class="line">                    <span class="string">"fd=%d, events=0x%x"</span>, fd, events);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// remove the callback</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        bool notify;</span><br><span class="line">        sp&lt;Connection&gt; connection = d-&gt;mConnectionsByFd.valueAt(connectionIndex);</span><br><span class="line">        <span class="keyword">if</span> (!(events &amp; (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!(events &amp; ALOOPER_EVENT_INPUT)) &#123;</span><br><span class="line">                ALOGW(<span class="string">"channel '%s' ~ Received spurious callback for unhandled poll event.  "</span></span><br><span class="line">                        <span class="string">"events=0x%x"</span>, connection-&gt;getInputChannelName(), events);</span><br><span class="line">                <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            nsecs_t currentTime = now();</span><br><span class="line">            bool gotOne = <span class="keyword">false</span>;</span><br><span class="line">            status_t status;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                uint32_t seq;</span><br><span class="line">                bool handled;</span><br><span class="line">                status = connection-&gt;inputPublisher.receiveFinishedSignal(&amp;seq, &amp;handled);</span><br><span class="line">                <span class="keyword">if</span> (status) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                d-&gt;finishDispatchCycleLocked(currentTime, connection, seq, handled);</span><br><span class="line">                gotOne = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (gotOne) &#123;</span><br><span class="line">                d-&gt;runCommandsLockedInterruptible();</span><br><span class="line">                <span class="keyword">if</span> (status == WOULD_BLOCK) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            notify = status != DEAD_OBJECT || !connection-&gt;monitor;</span><br><span class="line">            <span class="keyword">if</span> (notify) &#123;</span><br><span class="line">                ALOGE(<span class="string">"channel '%s' ~ Failed to receive finished signal.  status=%d"</span>,</span><br><span class="line">                        connection-&gt;getInputChannelName(), status);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// Monitor channels are never explicitly unregistered.</span></span><br><span class="line">            <span class="comment">// We do it automatically when the remote endpoint is closed so don't warn</span></span><br><span class="line">            <span class="comment">// about them.</span></span><br><span class="line">            notify = !connection-&gt;monitor;</span><br><span class="line">            <span class="keyword">if</span> (notify) &#123;</span><br><span class="line">                ALOGW(<span class="string">"channel '%s' ~ Consumer closed input channel or an error occurred.  "</span></span><br><span class="line">                        <span class="string">"events=0x%x"</span>, connection-&gt;getInputChannelName(), events);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Unregister the channel.</span></span><br><span class="line">        d-&gt;unregisterInputChannelLocked(connection-&gt;inputChannel, notify);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// remove the callback</span></span><br><span class="line">    &#125; <span class="comment">// release lock</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当收到的status为OK时，会调用finishDispatchCycleLocked()来完成一个消息的处理：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">InputDispatcher::finishDispatchCycleLocked()</span><br><span class="line">└── InputDispatcher::onDispatchCycleFinishedLocked()</span><br><span class="line">    └── InputDispatcher::doDispatchCycleFinishedLockedInterruptible()</span><br><span class="line">        └── InputDispatcher::startDispatchCycleLocked()</span><br></pre></td></tr></table></figure><p>调用到doDispatchCycleFinishedLockedInterruptible() 方法时，会将已经成功派发的消息从waitQueue中移除， 进一步调用会 startDispatchCycleLocked开始派发新的事件。</p><p>至此，我们回答了第一个问题：</p><p>一个正常的输入事件会经过从outboundQueue挪到waitQueue的过程，表示消息已经派发出去;再经过从waitQueue中移除的过程，表示消息已经被窗口接收。InputDispatcher作为中枢，不停地在递送着输入事件，当一个事件无法得到处理的时候，InputDispatcher不能就此死掉啊，否则系统也太容易崩溃了。 InputDispatcher 的策略是放弃掉处理不过来的事件， 并发出通知(这个通知机制就是ANR)，继续进行下一轮消息的处理。</p><blockquote><p>理解输入事件分发模型，我们可以举一个生活中的例子：<br>每一个输入事件可以比做一个快递，InputDispatcher就像一个快递中转站，窗口就像是收件人，InputChannel就像是快递员。 所有快递都会经过中转站中处理，中转站需要知道每一个快递的收件人是谁，通过快递员将快递发送到具体的收件人。 这其中有很多场景导致快递不能及时送到：譬如联系不到收件人;快递很多，快递员会忙不过来;快递员受伤休假了等等… 这时候快递员就需要告知中转站：有快递无法及时送到了。中转站在收到快递员的通知后，一边继续派发其他快递，一边报告上级。</p></blockquote><p>在了解输入事件分发模型之后，我们可以见识一下ANR机制了。在派发事件时，dispatchKeyLocked()和dispatchMotionLocked()， 需要找到当前的焦点窗口 ,焦点窗口才是最终接收事 件的地方，找窗口的过程就会判断是否已经发生了ANR：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">InputDispatcher::findFocusedWindowTargetsLocked()</span><br><span class="line">InputDispatcher::findTouchedWindowTargetsLocked()</span><br><span class="line">└── InputDispatcher::handleTargetsNotReadyLocked()</span><br><span class="line">    └── InputDispatcher::onANRLocked()</span><br><span class="line">        └── InputDispatcher::doNotifyANRLockedInterruptible()</span><br><span class="line">            └── NativeInputManager::notifyANR()</span><br></pre></td></tr></table></figure><ul><li><p>首先，会调用findFocusedWindowTargetsLocked()或findTouchedWindowTargetsLocked()寻找接收输入事件的窗口。在找到窗口以后，会调用checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件，会有一系列的场景阻碍事件的继续派发：</p><p>   • <strong>场景1:</strong> 窗口处于paused状态，不能处理输入事件“Waiting because the [targetType] window is paused.”</p><p>   • <strong>场景2:</strong> 窗口还未向InputDispatcher注册，无法将事件派发到窗口“Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.”</p><p>   • <strong>场景3:</strong> 窗口和InputDispatcher的连接已经中断，即InputChannel不能正常工作“Waiting because the [targetType] window’s input connection is [status]. The window may be in the process of being removed.”</p><p>   • <strong>场景4:</strong> InputChannel已经饱和，不能再处理新的事件“Waiting because the [targetType] window’s input channel is full. Outbound queue length: %d. Wait queue length: %d.”</p><p>   • <strong>场景5:</strong> 对于按键类型(KeyEvent)的输入事件，需要等待上一个事件处理完毕“Waiting to send key event because the [targetType] window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: %d. Wait queue length: %d.”</p><p>   • <strong>场景6:</strong> 对于触摸类型(TouchEvent)的输入事件，可以立即派发到当前的窗口，因为TouchEvent都是发生在用户当前可见的窗口。但有一种情况，如果当前应用由于队列有太多的输入事件等待派发，导致发生了ANR，那TouchEvent事件就需要排队等待派发。“Waiting to send non-key event because the %s window has not finished processing certain input events that were delivered to it over %0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.”</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line">String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; windowHandle, <span class="keyword">const</span> EventEntry* eventEntry,</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">char</span>* targetType) &#123;</span><br><span class="line">    <span class="comment">// If the window is paused then keep waiting.</span></span><br><span class="line">    <span class="keyword">if</span> (windowHandle-&gt;getInfo()-&gt;paused) &#123;</span><br><span class="line">        <span class="keyword">return</span> String8::format(<span class="string">"Waiting because the %s window is paused."</span>, targetType);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If the window's connection is not registered then keep waiting.</span></span><br><span class="line">    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle-&gt;getInputChannel());</span><br><span class="line">    <span class="keyword">if</span> (connectionIndex &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> String8::format(<span class="string">"Waiting because the %s window's input channel is not "</span></span><br><span class="line">                <span class="string">"registered with the input dispatcher.  The window may be in the process "</span></span><br><span class="line">                <span class="string">"of being removed."</span>, targetType);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If the connection is dead then keep waiting.</span></span><br><span class="line">    sp&lt;Connection&gt; connection = mConnectionsByFd.valueAt(connectionIndex);</span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;status != Connection::STATUS_NORMAL) &#123;</span><br><span class="line">        <span class="keyword">return</span> String8::format(<span class="string">"Waiting because the %s window's input connection is %s."</span></span><br><span class="line">                <span class="string">"The window may be in the process of being removed."</span>, targetType,</span><br><span class="line">                connection-&gt;getStatusLabel());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If the connection is backed up then keep waiting.</span></span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;inputPublisherBlocked) &#123;</span><br><span class="line">        <span class="keyword">return</span> String8::format(<span class="string">"Waiting because the %s window's input channel is full.  "</span></span><br><span class="line">                <span class="string">"Outbound queue length: %d.  Wait queue length: %d."</span>,</span><br><span class="line">                targetType, connection-&gt;outboundQueue.count(), connection-&gt;waitQueue.count());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Ensure that the dispatch queues aren't too far backed up for this event.</span></span><br><span class="line">    <span class="keyword">if</span> (eventEntry-&gt;type == EventEntry::TYPE_KEY) &#123;</span><br><span class="line">        <span class="comment">// If the event is a key event, then we must wait for all previous events to</span></span><br><span class="line">        <span class="comment">// complete before delivering it because previous events may have the</span></span><br><span class="line">        <span class="comment">// side-effect of transferring focus to a different window and we want to</span></span><br><span class="line">        <span class="comment">// ensure that the following keys are sent to the new window.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Suppose the user touches a button in a window then immediately presses "A".</span></span><br><span class="line">        <span class="comment">// If the button causes a pop-up window to appear then we want to ensure that</span></span><br><span class="line">        <span class="comment">// the "A" key is delivered to the new pop-up window.  This is because users</span></span><br><span class="line">        <span class="comment">// often anticipate pending UI changes when typing on a keyboard.</span></span><br><span class="line">        <span class="comment">// To obtain this behavior, we must serialize key events with respect to all</span></span><br><span class="line">        <span class="comment">// prior input events.</span></span><br><span class="line">        <span class="keyword">if</span> (!connection-&gt;outboundQueue.isEmpty() || !connection-&gt;waitQueue.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> String8::format(<span class="string">"Waiting to send key event because the %s window has not "</span></span><br><span class="line">                    <span class="string">"finished processing all of the input events that were previously "</span></span><br><span class="line">                    <span class="string">"delivered to it.  Outbound queue length: %d.  Wait queue length: %d."</span>,</span><br><span class="line">                    targetType, connection-&gt;outboundQueue.count(), connection-&gt;waitQueue.count());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// Touch events can always be sent to a window immediately because the user intended</span></span><br><span class="line">        <span class="comment">// to touch whatever was visible at the time.  Even if focus changes or a new</span></span><br><span class="line">        <span class="comment">// window appears moments later, the touch event was meant to be delivered to</span></span><br><span class="line">        <span class="comment">// whatever window happened to be on screen at the time.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Generic motion events, such as trackball or joystick events are a little trickier.</span></span><br><span class="line">        <span class="comment">// Like key events, generic motion events are delivered to the focused window.</span></span><br><span class="line">        <span class="comment">// Unlike key events, generic motion events don't tend to transfer focus to other</span></span><br><span class="line">        <span class="comment">// windows and it is not important for them to be serialized.  So we prefer to deliver</span></span><br><span class="line">        <span class="comment">// generic motion events as soon as possible to improve efficiency and reduce lag</span></span><br><span class="line">        <span class="comment">// through batching.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// The one case where we pause input event delivery is when the wait queue is piling</span></span><br><span class="line">        <span class="comment">// up with lots of events because the application is not responding.</span></span><br><span class="line">        <span class="comment">// This condition ensures that ANRs are detected reliably.</span></span><br><span class="line">        <span class="keyword">if</span> (!connection-&gt;waitQueue.isEmpty()</span><br><span class="line">                &amp;&amp; currentTime &gt;= connection-&gt;waitQueue.head-&gt;deliveryTime</span><br><span class="line">                        + STREAM_AHEAD_EVENT_TIMEOUT) &#123;</span><br><span class="line">            <span class="keyword">return</span> String8::format(<span class="string">"Waiting to send non-key event because the %s window has not "</span></span><br><span class="line">                    <span class="string">"finished processing certain input events that were delivered to it over "</span></span><br><span class="line">                    <span class="string">"%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms."</span>,</span><br><span class="line">                    targetType, STREAM_AHEAD_EVENT_TIMEOUT * <span class="number">0.000001f</span>,</span><br><span class="line">                    connection-&gt;waitQueue.count(),</span><br><span class="line">                    (currentTime - connection-&gt;waitQueue.head-&gt;deliveryTime) * <span class="number">0.000001f</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> String8::empty();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>然后，上述有任何一个场景发生了，则输入事件需要继续等待，紧接着就会调用handleTargetsNotReadyLocked()来判断是不是已经的等待超时了：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line">int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,</span><br><span class="line">        <span class="keyword">const</span> EventEntry* entry,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputApplicationHandle&gt;&amp; applicationHandle,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; windowHandle,</span><br><span class="line">        nsecs_t* nextWakeupTime, <span class="keyword">const</span> <span class="keyword">char</span>* reason) &#123;</span><br><span class="line">    <span class="keyword">if</span> (applicationHandle == NULL &amp;&amp; windowHandle == NULL) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) &#123;</span><br><span class="line">#if DEBUG_FOCUS</span><br><span class="line">            ALOGD(<span class="string">"Waiting for system to become ready for input.  Reason: %s"</span>, reason);</span><br><span class="line">#endif</span><br><span class="line">            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;</span><br><span class="line">            mInputTargetWaitStartTime = currentTime;</span><br><span class="line">            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;</span><br><span class="line">            mInputTargetWaitTimeoutExpired = <span class="keyword">false</span>;</span><br><span class="line">            mInputTargetWaitApplicationHandle.clear();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) &#123;</span><br><span class="line">#if DEBUG_FOCUS</span><br><span class="line">            ALOGD(<span class="string">"Waiting for application to become ready for input: %s.  Reason: %s"</span>,</span><br><span class="line">                    getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),</span><br><span class="line">                    reason);</span><br><span class="line">#endif</span><br><span class="line">            nsecs_t timeout;</span><br><span class="line">            <span class="keyword">if</span> (windowHandle != NULL) &#123;</span><br><span class="line">                timeout = windowHandle-&gt;getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (applicationHandle != NULL) &#123;</span><br><span class="line">                timeout = applicationHandle-&gt;getDispatchingTimeout(</span><br><span class="line">                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;</span><br><span class="line">            mInputTargetWaitStartTime = currentTime;</span><br><span class="line">            mInputTargetWaitTimeoutTime = currentTime + timeout;</span><br><span class="line">            mInputTargetWaitTimeoutExpired = <span class="keyword">false</span>;</span><br><span class="line">            mInputTargetWaitApplicationHandle.clear();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (windowHandle != NULL) &#123;</span><br><span class="line">                mInputTargetWaitApplicationHandle = windowHandle-&gt;inputApplicationHandle;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mInputTargetWaitApplicationHandle == NULL &amp;&amp; applicationHandle != NULL) &#123;</span><br><span class="line">                mInputTargetWaitApplicationHandle = applicationHandle;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mInputTargetWaitTimeoutExpired) &#123;</span><br><span class="line">        <span class="keyword">return</span> INPUT_EVENT_INJECTION_TIMED_OUT;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (currentTime &gt;= mInputTargetWaitTimeoutTime) &#123;</span><br><span class="line">        onANRLocked(currentTime, applicationHandle, windowHandle,</span><br><span class="line">                entry-&gt;eventTime, mInputTargetWaitStartTime, reason);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Force poll loop to wake up immediately on next iteration once we get the</span></span><br><span class="line">        <span class="comment">// ANR response back from the policy.</span></span><br><span class="line">        *nextWakeupTime = LONG_LONG_MIN;</span><br><span class="line">        <span class="keyword">return</span> INPUT_EVENT_INJECTION_PENDING;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// Force poll loop to wake up when timeout is due.</span></span><br><span class="line">        <span class="keyword">if</span> (mInputTargetWaitTimeoutTime &lt; *nextWakeupTime) &#123;</span><br><span class="line">            *nextWakeupTime = mInputTargetWaitTimeoutTime;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> INPUT_EVENT_INJECTION_PENDING;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>最后，如果当前事件派发已经超时，则说明已经检测到了ANR，调用onANRLocked()方法，然后将nextWakeupTime设置为最小值，马上开始下一轮调度。 在onANRLocked()方法中， 会保存ANR的一些状态信息，调用doNotifyANRLockedInterruptible()，进一步会调用到JNI层的 NativeInputManager::notifyANR()方法， 它的主要功能就是衔接Native层和  Java层，直接调用Java层的InputManagerService.notifyANR()方法。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> InputDispatcher::onANRLocked(</span><br><span class="line">        nsecs_t currentTime, <span class="keyword">const</span> sp&lt;InputApplicationHandle&gt;&amp; applicationHandle,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; windowHandle,</span><br><span class="line">        nsecs_t eventTime, nsecs_t waitStartTime, <span class="keyword">const</span> <span class="keyword">char</span>* reason) &#123;</span><br><span class="line">    <span class="keyword">float</span> dispatchLatency = (currentTime - eventTime) * <span class="number">0.000001f</span>;</span><br><span class="line">    <span class="keyword">float</span> waitDuration = (currentTime - waitStartTime) * <span class="number">0.000001f</span>;</span><br><span class="line">    ALOGI(<span class="string">"Application is not responding: %s.  "</span></span><br><span class="line">            <span class="string">"It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s"</span>,</span><br><span class="line">            getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),</span><br><span class="line">            dispatchLatency, waitDuration, reason);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Capture a record of the InputDispatcher state at the time of the ANR.</span></span><br><span class="line">    time_t t = time(NULL);</span><br><span class="line">    struct tm tm;</span><br><span class="line">    localtime_r(&amp;t, &amp;tm);</span><br><span class="line">    <span class="keyword">char</span> timestr[<span class="number">64</span>];</span><br><span class="line">    strftime(timestr, sizeof(timestr), <span class="string">"%F %T"</span>, &amp;tm);</span><br><span class="line">    mLastANRState.clear();</span><br><span class="line">    mLastANRState.append(INDENT <span class="string">"ANR:\n"</span>);</span><br><span class="line">    mLastANRState.appendFormat(INDENT2 <span class="string">"Time: %s\n"</span>, timestr);</span><br><span class="line">    mLastANRState.appendFormat(INDENT2 <span class="string">"Window: %s\n"</span>,</span><br><span class="line">            getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());</span><br><span class="line">    mLastANRState.appendFormat(INDENT2 <span class="string">"DispatchLatency: %0.1fms\n"</span>, dispatchLatency);</span><br><span class="line">    mLastANRState.appendFormat(INDENT2 <span class="string">"WaitDuration: %0.1fms\n"</span>, waitDuration);</span><br><span class="line">    mLastANRState.appendFormat(INDENT2 <span class="string">"Reason: %s\n"</span>, reason);</span><br><span class="line">    dumpDispatchStateLocked(mLastANRState);</span><br><span class="line"></span><br><span class="line">    CommandEntry* commandEntry = postCommandLocked(</span><br><span class="line">            &amp; InputDispatcher::doNotifyANRLockedInterruptible);</span><br><span class="line">    commandEntry-&gt;inputApplicationHandle = applicationHandle;</span><br><span class="line">    commandEntry-&gt;inputWindowHandle = windowHandle;</span><br><span class="line">    commandEntry-&gt;reason = reason;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> InputDispatcher::doNotifyANRLockedInterruptible(</span><br><span class="line">        CommandEntry* commandEntry) &#123;</span><br><span class="line">    mLock.unlock();</span><br><span class="line"></span><br><span class="line">    nsecs_t newTimeout = mPolicy-&gt;notifyANR(</span><br><span class="line">            commandEntry-&gt;inputApplicationHandle, commandEntry-&gt;inputWindowHandle,</span><br><span class="line">            commandEntry-&gt;reason);</span><br><span class="line"></span><br><span class="line">    mLock.lock();</span><br><span class="line"></span><br><span class="line">    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,</span><br><span class="line">            commandEntry-&gt;inputWindowHandle != NULL</span><br><span class="line">                    ? commandEntry-&gt;inputWindowHandle-&gt;getInputChannel() : NULL);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">InputDispatcher::InputDispatcher(<span class="keyword">const</span> sp&lt;InputDispatcherPolicyInterface&gt;&amp; policy) :</span><br><span class="line">    mPolicy(policy),</span><br><span class="line">    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),</span><br><span class="line">    mAppSwitchSawKeyDown(<span class="keyword">false</span>), mAppSwitchDueTime(LONG_LONG_MAX),</span><br><span class="line">    mNextUnblockedEvent(NULL),</span><br><span class="line">    mDispatchEnabled(<span class="keyword">false</span>), mDispatchFrozen(<span class="keyword">false</span>), mInputFilterEnabled(<span class="keyword">false</span>),</span><br><span class="line">    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) &#123;</span><br><span class="line">    mLooper = <span class="keyword">new</span> Looper(<span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">    mKeyRepeatState.lastKeyEntry = NULL;</span><br><span class="line"></span><br><span class="line">    policy-&gt;getDispatcherConfiguration(&amp;mConfig);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看到mPolicy是InputDispatcherPolicyInterface,对应的在JNI层frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line">class NativeInputManager : public virtual RefBase,</span><br><span class="line">    <span class="keyword">public</span> virtual InputReaderPolicyInterface,</span><br><span class="line">    <span class="keyword">public</span> virtual InputDispatcherPolicyInterface,</span><br><span class="line">    <span class="keyword">public</span> virtual PointerControllerPolicyInterface &#123;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    virtual ~NativeInputManager();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    NativeInputManager(jobject contextObj, jobject serviceObj, <span class="keyword">const</span> sp&lt;Looper&gt;&amp; looper);</span><br><span class="line"></span><br><span class="line">    <span class="function">inline sp&lt;InputManager&gt; <span class="title">getInputManager</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123; <span class="keyword">return</span> mInputManager; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dump</span><span class="params">(String8&amp; dump)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setVirtualDisplayViewports</span><span class="params">(JNIEnv* env, jobjectArray viewportObjArray)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setDisplayViewport</span><span class="params">(int32_t viewportType, <span class="keyword">const</span> DisplayViewport&amp; viewport)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">status_t <span class="title">registerInputChannel</span><span class="params">(JNIEnv* env, <span class="keyword">const</span> sp&lt;InputChannel&gt;&amp; inputChannel,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle, bool monitor)</span></span>;</span><br><span class="line">    <span class="function">status_t <span class="title">unregisterInputChannel</span><span class="params">(JNIEnv* env, <span class="keyword">const</span> sp&lt;InputChannel&gt;&amp; inputChannel)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setInputWindows</span><span class="params">(JNIEnv* env, jobjectArray windowHandleObjArray)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setFocusedApplication</span><span class="params">(JNIEnv* env, jobject applicationHandleObj)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setInputDispatchMode</span><span class="params">(bool enabled, bool frozen)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setSystemUiVisibility</span><span class="params">(int32_t visibility)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setPointerSpeed</span><span class="params">(int32_t speed)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setInputDeviceEnabled</span><span class="params">(uint32_t deviceId, bool enabled)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setShowTouches</span><span class="params">(bool enabled)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setInteractive</span><span class="params">(bool interactive)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">reloadCalibration</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setPointerIconType</span><span class="params">(int32_t iconId)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">reloadPointerIcons</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setCustomPointerIcon</span><span class="params">(<span class="keyword">const</span> SpriteIcon&amp; icon)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setPointerCapture</span><span class="params">(bool enabled)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* --- InputReaderPolicyInterface implementation --- */</span></span><br><span class="line"></span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">getReaderConfiguration</span><span class="params">(InputReaderConfiguration* outConfig)</span></span>;</span><br><span class="line">    <span class="function">virtual sp&lt;PointerControllerInterface&gt; <span class="title">obtainPointerController</span><span class="params">(int32_t deviceId)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">notifyInputDevicesChanged</span><span class="params">(<span class="keyword">const</span> Vector&lt;InputDeviceInfo&gt;&amp; inputDevices)</span></span>;</span><br><span class="line">    <span class="function">virtual sp&lt;KeyCharacterMap&gt; <span class="title">getKeyboardLayoutOverlay</span><span class="params">(<span class="keyword">const</span> InputDeviceIdentifier&amp; identifier)</span></span>;</span><br><span class="line">    <span class="function">virtual String8 <span class="title">getDeviceAlias</span><span class="params">(<span class="keyword">const</span> InputDeviceIdentifier&amp; identifier)</span></span>;</span><br><span class="line">    <span class="function">virtual TouchAffineTransformation <span class="title">getTouchAffineTransformation</span><span class="params">(JNIEnv *env,</span></span></span><br><span class="line"><span class="function"><span class="params">            jfloatArray matrixArr)</span></span>;</span><br><span class="line">    <span class="function">virtual TouchAffineTransformation <span class="title">getTouchAffineTransformation</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> String8&amp; inputDeviceDescriptor, int32_t surfaceRotation)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* --- InputDispatcherPolicyInterface implementation --- */</span></span><br><span class="line"></span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">notifySwitch</span><span class="params">(nsecs_t when, uint32_t switchValues, uint32_t switchMask,</span></span></span><br><span class="line"><span class="function"><span class="params">            uint32_t policyFlags)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">notifyConfigurationChanged</span><span class="params">(nsecs_t when)</span></span>;</span><br><span class="line">    <span class="function">virtual nsecs_t <span class="title">notifyANR</span><span class="params">(<span class="keyword">const</span> sp&lt;InputApplicationHandle&gt;&amp; inputApplicationHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> String8&amp; reason)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">notifyInputChannelBroken</span><span class="params">(<span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle)</span></span>;</span><br><span class="line">    <span class="function">virtual bool <span class="title">filterInputEvent</span><span class="params">(<span class="keyword">const</span> InputEvent* inputEvent, uint32_t policyFlags)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">getDispatcherConfiguration</span><span class="params">(InputDispatcherConfiguration* outConfig)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">interceptKeyBeforeQueueing</span><span class="params">(<span class="keyword">const</span> KeyEvent* keyEvent, uint32_t&amp; policyFlags)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">interceptMotionBeforeQueueing</span><span class="params">(nsecs_t when, uint32_t&amp; policyFlags)</span></span>;</span><br><span class="line">    <span class="function">virtual nsecs_t <span class="title">interceptKeyBeforeDispatching</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> KeyEvent* keyEvent, uint32_t policyFlags)</span></span>;</span><br><span class="line">    <span class="function">virtual bool <span class="title">dispatchUnhandledKey</span><span class="params">(<span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">pokeUserActivity</span><span class="params">(nsecs_t eventTime, int32_t eventType)</span></span>;</span><br><span class="line">    <span class="function">virtual bool <span class="title">checkInjectEventsPermissionNonReentrant</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            int32_t injectorPid, int32_t injectorUid)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* --- PointerControllerPolicyInterface implementation --- */</span></span><br><span class="line"></span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">loadPointerIcon</span><span class="params">(SpriteIcon* icon)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">loadPointerResources</span><span class="params">(PointerResources* outResources)</span></span>;</span><br><span class="line">    <span class="function">virtual <span class="keyword">void</span> <span class="title">loadAdditionalMouseResources</span><span class="params">(std::map&lt;int32_t, SpriteIcon&gt;* outResources,</span></span></span><br><span class="line"><span class="function"><span class="params">            std::map&lt;int32_t, PointerAnimation&gt;* outAnimationResources)</span></span>;</span><br><span class="line">    <span class="function">virtual int32_t <span class="title">getDefaultPointerIconId</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function">virtual int32_t <span class="title">getCustomPointerIconId</span><span class="params">()</span></span>;</span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p>JNI层中notifyANR的实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">nsecs_t NativeInputManager::notifyANR(<span class="keyword">const</span> sp&lt;InputApplicationHandle&gt;&amp; inputApplicationHandle,</span><br><span class="line">        <span class="keyword">const</span> sp&lt;InputWindowHandle&gt;&amp; inputWindowHandle, <span class="keyword">const</span> String8&amp; reason) &#123;</span><br><span class="line">#if DEBUG_INPUT_DISPATCHER_POLICY</span><br><span class="line">    ALOGD(<span class="string">"notifyANR"</span>);</span><br><span class="line">#endif</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line"></span><br><span class="line">    JNIEnv* env = jniEnv();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将应用程序句柄、窗口句柄、ANR原因字符串，转化为Java层的对象</span></span><br><span class="line">    jobject inputApplicationHandleObj =</span><br><span class="line">            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);</span><br><span class="line">    jobject inputWindowHandleObj =</span><br><span class="line">            getInputWindowHandleObjLocalRef(env, inputWindowHandle);</span><br><span class="line">    jstring reasonObj = env-&gt;NewStringUTF(reason.string());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用Java层的InputManagerService.notifyANR()方法</span></span><br><span class="line">    jlong newTimeout = env-&gt;CallLongMethod(mServiceObj,</span><br><span class="line">                gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,</span><br><span class="line">                reasonObj);</span><br><span class="line">    <span class="keyword">if</span> (checkAndClearExceptionFromCallback(env, <span class="string">"notifyANR"</span>)) &#123;</span><br><span class="line">        newTimeout = <span class="number">0</span>; <span class="comment">// abort dispatch</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">assert</span>(newTimeout &gt;= <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    env-&gt;DeleteLocalRef(reasonObj);</span><br><span class="line">    env-&gt;DeleteLocalRef(inputWindowHandleObj);</span><br><span class="line">    env-&gt;DeleteLocalRef(inputApplicationHandleObj);</span><br><span class="line">    <span class="keyword">return</span> newTimeout;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，ANR的处理逻辑转交到了Java层。底层(Native)发现一旦有输入事件派发超时，就会通知上层(Java)，上层收到ANR通知后，决定是否终止当前输入事件的派发。</p><p>发生ANR时，Java层最开始的入口是InputManagerService.notifyANR()，它是直接被Native层调用的。我们先把ANR的Java层调用关系列出来：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">InputManagerService.notifyANR()</span><br><span class="line">└── InputMonitor.notifyANR()</span><br><span class="line">    ├── IApplicationToken.keyDispatchingTimedOut()</span><br><span class="line">    │   └── ActivityRecord.keyDispatchingTimedOut()</span><br><span class="line">    │       └── AMS.inputDispatchingTimedOut()</span><br><span class="line">    │           └── AMS.appNotResponding()</span><br><span class="line">    │</span><br><span class="line">    └── AMS.inputDispatchingTimedOut()</span><br><span class="line">        └── AMS.appNotResponding()</span><br></pre></td></tr></table></figure><p>在Java层的InputManagerService代码: frameworks/base/services/core/java/com/android/server/input/InputManagerService.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> WindowManagerCallbacks mWindowManagerCallbacks;</span><br><span class="line">....</span><br><span class="line"> <span class="comment">// Native callback.</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">long</span> <span class="title">notifyANR</span><span class="params">(InputApplicationHandle inputApplicationHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">         InputWindowHandle inputWindowHandle, String reason)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">return</span> mWindowManagerCallbacks.notifyANR(</span><br><span class="line">             inputApplicationHandle, inputWindowHandle, reason);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>WindowManagerCallbacks由 InputMonitor.java实现： frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java<br>InputManagerService.notifyANR()只是为Native层定义了一个接口，它直接调用InputMonitor.notifyANR()。 如果该方法的返回值等于0, 则放弃本次输入事件;如果大于0, 则表示 需要继续等待的时间。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">InputMonitor</span> <span class="keyword">implements</span> <span class="title">InputManagerService</span>.<span class="title">WindowManagerCallbacks</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> WindowManagerService mService;</span><br><span class="line">   ....</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Notifies the window manager about an application that is not responding.</span></span><br><span class="line"><span class="comment">     * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Called by the InputManager.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">notifyANR</span><span class="params">(InputApplicationHandle inputApplicationHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">            InputWindowHandle inputWindowHandle, String reason)</span> </span>&#123;</span><br><span class="line">        AppWindowToken appWindowToken = <span class="keyword">null</span>;</span><br><span class="line">        WindowState windowState = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">boolean</span> aboveSystem = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">synchronized</span> (mService.mWindowMap) &#123;</span><br><span class="line">            <span class="keyword">if</span> (inputWindowHandle != <span class="keyword">null</span>) &#123;</span><br><span class="line">                windowState = (WindowState) inputWindowHandle.windowState;</span><br><span class="line">                <span class="keyword">if</span> (windowState != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    appWindowToken = windowState.mAppToken;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (appWindowToken == <span class="keyword">null</span> &amp;&amp; inputApplicationHandle != <span class="keyword">null</span>) &#123;</span><br><span class="line">                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (windowState != <span class="keyword">null</span>) &#123;</span><br><span class="line">                Slog.i(TAG_WM, <span class="string">"Input event dispatching timed out "</span></span><br><span class="line">                        + <span class="string">"sending to "</span> + windowState.mAttrs.getTitle()</span><br><span class="line">                        + <span class="string">".  Reason: "</span> + reason);</span><br><span class="line">                <span class="comment">// Figure out whether this window is layered above system windows.</span></span><br><span class="line">                <span class="comment">// We need to do this here to help the activity manager know how to</span></span><br><span class="line">                <span class="comment">// layer its ANR dialog.</span></span><br><span class="line">                <span class="keyword">int</span> systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(</span><br><span class="line">                        TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);</span><br><span class="line">                aboveSystem = windowState.mBaseLayer &gt; systemAlertLayer;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (appWindowToken != <span class="keyword">null</span>) &#123;</span><br><span class="line">                Slog.i(TAG_WM, <span class="string">"Input event dispatching timed out "</span></span><br><span class="line">                        + <span class="string">"sending to application "</span> + appWindowToken.stringName</span><br><span class="line">                        + <span class="string">".  Reason: "</span> + reason);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                Slog.i(TAG_WM, <span class="string">"Input event dispatching timed out "</span></span><br><span class="line">                        + <span class="string">".  Reason: "</span> + reason);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mService.saveANRStateLocked(appWindowToken, windowState, reason);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// All the calls below need to happen without the WM lock held since they call into AM.</span></span><br><span class="line">        mService.mAmInternal.saveANRState(reason);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (appWindowToken != <span class="keyword">null</span> &amp;&amp; appWindowToken.appToken != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// appToken实际上就是当前的ActivityRecord。</span></span><br><span class="line">            <span class="comment">// 如果发生ANR的Activity还存在，则直接通过ActivityRecord通知事件派发超时</span></span><br><span class="line">            <span class="comment">// Notify the activity manager about the timeout and let it decide whether</span></span><br><span class="line">            <span class="comment">// to abort dispatching or keep waiting.</span></span><br><span class="line">            <span class="keyword">final</span> AppWindowContainerController controller = appWindowToken.getController();</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">boolean</span> abort = controller != <span class="keyword">null</span></span><br><span class="line">                    &amp;&amp; controller.keyDispatchingTimedOut(reason,</span><br><span class="line">                            (windowState != <span class="keyword">null</span>) ? windowState.mSession.mPid : -<span class="number">1</span>);</span><br><span class="line">            <span class="keyword">if</span> (!abort) &#123;</span><br><span class="line">                <span class="comment">// The activity manager declined to abort dispatching.</span></span><br><span class="line">                <span class="comment">// Wait a bit longer and timeout again later.</span></span><br><span class="line">                <span class="keyword">return</span> appWindowToken.mInputDispatchingTimeoutNanos;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (windowState != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果发生ANR的Activity已经销毁了，则通过AMS通知事件派发超时</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// Notify the activity manager about the timeout and let it decide whether</span></span><br><span class="line">                <span class="comment">// to abort dispatching or keep waiting.</span></span><br><span class="line">                <span class="keyword">long</span> timeout = ActivityManager.getService().inputDispatchingTimedOut(</span><br><span class="line">                        windowState.mSession.mPid, aboveSystem, reason);</span><br><span class="line">                <span class="keyword">if</span> (timeout &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="comment">// The activity manager declined to abort dispatching.</span></span><br><span class="line">                    <span class="comment">// Wait a bit longer and timeout again later.</span></span><br><span class="line">                    <span class="keyword">return</span> timeout * <span class="number">1000000L</span>; <span class="comment">// nanoseconds</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException ex) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// abort dispatching</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>上述方法中有两种不同的调用方式，但最终都会交由AMS.inputDispatchingTimedOut()处理。AMS有重载的inputDispatchingTimedOut()方法，他们的参数不一样。 ActivityRecord 调用时，可以传入的信息更多一点(当前发生ANR的界面是哪一个)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">inputDispatchingTimedOut</span><span class="params">(<span class="keyword">int</span> pid, <span class="keyword">final</span> <span class="keyword">boolean</span> aboveSystem, String reason)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)</span><br><span class="line">                != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(<span class="string">"Requires permission "</span></span><br><span class="line">                    + android.Manifest.permission.FILTER_EVENTS);</span><br><span class="line">        &#125;</span><br><span class="line">        ProcessRecord proc;</span><br><span class="line">        <span class="keyword">long</span> timeout;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (mPidsSelfLocked) &#123;</span><br><span class="line">                proc = mPidsSelfLocked.get(pid);<span class="comment">// 1. 根据进程号获取到ProcessRecord</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 2. 获取超时时间</span></span><br><span class="line">            <span class="comment">// 测试环境下的超时时间是INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT(60秒)，</span></span><br><span class="line">            <span class="comment">// 正常环境下的超时时间是KEY_DISPATCHING_TIMEOUT(5秒)</span></span><br><span class="line">            timeout = getInputDispatchingTimeoutLocked(proc);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用重载的函数，如果返回True，则表示需要中断当前的事件派发;</span></span><br><span class="line">        <span class="keyword">if</span> (inputDispatchingTimedOut(proc, <span class="keyword">null</span>, <span class="keyword">null</span>, aboveSystem, reason)) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3. 返回继续等待的时间，这个值会传递到Native层</span></span><br><span class="line">        <span class="keyword">return</span> timeout;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Handle input dispatching timeouts.</span></span><br><span class="line"><span class="comment">     * Returns whether input dispatching should be aborted or not.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">inputDispatchingTimedOut</span><span class="params">(<span class="keyword">final</span> ProcessRecord proc,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">final</span> ActivityRecord activity, <span class="keyword">final</span> ActivityRecord parent,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">final</span> <span class="keyword">boolean</span> aboveSystem, String reason)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)</span><br><span class="line">                != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> SecurityException(<span class="string">"Requires permission "</span></span><br><span class="line">                    + android.Manifest.permission.FILTER_EVENTS);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> String annotation;</span><br><span class="line">        <span class="keyword">if</span> (reason == <span class="keyword">null</span>) &#123;</span><br><span class="line">            annotation = <span class="string">"Input dispatching timed out"</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            annotation = <span class="string">"Input dispatching timed out ("</span> + reason + <span class="string">")"</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (proc != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (proc.debugging) &#123;<span class="comment">// 1. 发生ANR进程正处于调试状态，不需要中断事件</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 2. 发生ANR的进程是测试进程，需要中断，但不在UI界面显示ANR信息判断</span></span><br><span class="line">                <span class="keyword">if</span> (proc.instr != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    Bundle info = <span class="keyword">new</span> Bundle();</span><br><span class="line">                    info.putString(<span class="string">"shortMsg"</span>, <span class="string">"keyDispatchingTimedOut"</span>);</span><br><span class="line">                    info.putString(<span class="string">"longMsg"</span>, annotation);</span><br><span class="line">                    finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 3. 通知UI界面显示ANR信息</span></span><br><span class="line">            mHandler.post(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>至此，我们回答了第二个问题：</p><p>在InputDispatcher派发输入事件时，会寻找接收事件的窗口，如果无法正常派发，则可能会导致当前需要派发的事件超时(默认是5秒)。 Native层发现超时了，会通知Java层，Java层经过一些处理后，会反馈给Native层，是继续等待还是丢弃当前派发的事件。</p><h4 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h4><p>ANR监测机制包含三种：</p><p>• * Service ANR，前台进程中Service生命周期不能超过20秒，后台进程中Service的生命周期不能超过200秒。 在启动Service时，抛出定时消息SERVICE_TIMEOUT_MSG或SERVICE_BACKGOURND_TIMEOUT_MSG，如果定时消息响应了，则说明发生了ANR</p><p>• * Broadcast ANR，前台的“串行广播消息”必须在10秒内处理完毕，后台的“串行广播消息”必须在60秒处理完毕， 每派发串行广播消息到一个接收器时，都会抛出一个定时消息BROADCAST_TIMEOUT_MSG，如果定时消息响应，则判断是否广播消息处理超时，超时就说明发生了ANR</p><p>• * Input ANR，输入事件必须在5秒内处理完毕。在派发一个输入事件时，会判断当前输入事件是否需要等待，如果需要等待，则判断是否等待已经超时，超时就说明发生了ANR</p><p>ANR监测机制实际上是对应用程序主线程的要求，要求主线成必须在限定的时间内，完成对几种操作的响应;否则，就可以认为应用程序主线程失去响应能力。</p><p>从ANR的三种监测机制中，我们看到不同超时机制的设计：</p><p>Service和Broadcast都是由AMS调度，利用Handler和Looper，设计了一个TIMEOUT消息交由AMS线程来处理，整个超时机制的实现都是在Java层； InputEvent由InputDispatcher调度，待处理的输入事件都会进入队列中等待，设计了一个等待超时的判断，超时机制的实现在Native层。</p><h3 id="ANR的报告机制"><a href="#ANR的报告机制" class="headerlink" title="ANR的报告机制"></a>ANR的报告机制</h3><p>无论哪种类型的ANR发生以后，最终都会调用 AMS.appNotResponding() 方法，所谓“殊途同归”。这个方法的职能就是向用户或开发者报告ANR发生了。 最终的表现形式是：弹出一个对话框，告诉用户当前某个程序无响应;输入一大堆与ANR相关的日志，便于开发者解决问题。</p><p>最终形式我们见过很多，但输出日志的原理是什么，未必所有人都了解，下面我们就来认识一下是如何输出ANR日志的。</p><p>在  <a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/AppErrors.java" target="_blank" rel="noopener">AppErrors.java</a>  frameworks/base/services/core/java/com/android/server/am/AppErrors.java中处理ANR的弹框<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">appNotResponding</span><span class="params">(ProcessRecord app, ActivityRecord activity,</span></span></span><br><span class="line"><span class="function"><span class="params">        ActivityRecord parent, <span class="keyword">boolean</span> aboveSystem, <span class="keyword">final</span> String annotation)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// app: 当前发生ANR的进程</span></span><br><span class="line"><span class="comment">// activity: 发生ANR的界面</span></span><br><span class="line"><span class="comment">// parent: 发生ANR的界面的上一级界面</span></span><br><span class="line"><span class="comment">// aboveSystem:</span></span><br><span class="line"><span class="comment">// annotation: 发生ANR的原因</span></span><br><span class="line">    ArrayList&lt;Integer&gt; firstPids = <span class="keyword">new</span> ArrayList&lt;Integer&gt;(<span class="number">5</span>);</span><br><span class="line">    SparseArray&lt;Boolean&gt; lastPids = <span class="keyword">new</span> SparseArray&lt;Boolean&gt;(<span class="number">20</span>);</span><br><span class="line">    <span class="keyword">if</span> (mService.mController != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 0 == continue, -1 = kill process immediately</span></span><br><span class="line">            <span class="keyword">int</span> res = mService.mController.appEarlyNotResponding(</span><br><span class="line">                    app.processName, app.pid, annotation);</span><br><span class="line">            <span class="keyword">if</span> (res &lt; <span class="number">0</span> &amp;&amp; app.pid != MY_PID) &#123;</span><br><span class="line">                app.kill(<span class="string">"anr"</span>, <span class="keyword">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">            mService.mController = <span class="keyword">null</span>;</span><br><span class="line">            Watchdog.getInstance().setActivityController(<span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">long</span> anrTime = SystemClock.uptimeMillis();</span><br><span class="line">    <span class="keyword">if</span> (ActivityManagerService.MONITOR_CPU_USAGE) &#123;<span class="comment">// 1. 更新CPU使用信息。ANR的第一次CPU信息采样</span></span><br><span class="line">        mService.updateCpuStatsNow();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Unless configured otherwise, swallow ANRs in background processes &amp; kill the process.</span></span><br><span class="line">    <span class="keyword">boolean</span> showBackground = Settings.Secure.getInt(mContext.getContentResolver(),</span><br><span class="line">            Settings.Secure.ANR_SHOW_BACKGROUND, <span class="number">0</span>) != <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">boolean</span> isSilentANR;</span><br><span class="line">    <span class="keyword">synchronized</span> (mService) &#123;</span><br><span class="line">        <span class="comment">// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.</span></span><br><span class="line">        <span class="keyword">if</span> (mService.mShuttingDown) &#123;<span class="comment">//正在关机过程中的ANR，直接忽视掉</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"During shutdown skipping ANR: "</span> + app + <span class="string">" "</span> + annotation);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (app.notResponding) &#123;<span class="comment">//重复的ANR，直接忽略掉，不重复弹框</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"Skipping duplicate ANR: "</span> + app + <span class="string">" "</span> + annotation);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (app.crashing) &#123;<span class="comment">//APP crash 引起的ANR，不弹框</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"Crashing app skipping ANR: "</span> + app + <span class="string">" "</span> + annotation);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (app.killedByAm) &#123;<span class="comment">// APP 已经被 AM kill掉的，不弹框</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"App already killed by AM skipping ANR: "</span> + app + <span class="string">" "</span> + annotation);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (app.killed) &#123;<span class="comment">//APP 已经挂掉了，不弹框</span></span><br><span class="line">            Slog.i(TAG, <span class="string">"Skipping died app ANR: "</span> + app + <span class="string">" "</span> + annotation);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// In case we come through here for the same app before completing</span></span><br><span class="line">        <span class="comment">// this one, mark as anring now so we will bail out.</span></span><br><span class="line">        app.notResponding = <span class="keyword">true</span>;</span><br><span class="line">        <span class="comment">// Log the ANR to the event log.</span></span><br><span class="line">        <span class="comment">//2.将ANR 信息写入event log中</span></span><br><span class="line">        EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,</span><br><span class="line">                app.processName, app.info.flags, annotation);</span><br><span class="line">        <span class="comment">// Dump thread traces as quickly as we can, starting with "interesting" processes.</span></span><br><span class="line">        <span class="comment">// 3. 填充firstPids和lastPids数组。从最近运行进程(Last Recently Used)中挑选：</span></span><br><span class="line"><span class="comment">//    firstPids用于保存ANR进程及其父进程，system_server进程和persistent的进程(譬如Phone进程)</span></span><br><span class="line"><span class="comment">//    lastPids用于保存除firstPids外的其他进程</span></span><br><span class="line">        firstPids.add(app.pid);</span><br><span class="line">        <span class="comment">// Don't dump other PIDs if it's a background ANR</span></span><br><span class="line">        isSilentANR = !showBackground &amp;&amp; !isInterestingForBackgroundTraces(app);</span><br><span class="line">        <span class="keyword">if</span> (!isSilentANR) &#123;</span><br><span class="line">            <span class="keyword">int</span> parentPid = app.pid;</span><br><span class="line">            <span class="keyword">if</span> (parent != <span class="keyword">null</span> &amp;&amp; parent.app != <span class="keyword">null</span> &amp;&amp; parent.app.pid &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                parentPid = parent.app.pid;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (parentPid != app.pid) firstPids.add(parentPid);</span><br><span class="line">            <span class="keyword">if</span> (MY_PID != app.pid &amp;&amp; MY_PID != parentPid) firstPids.add(MY_PID);</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = mService.mLruProcesses.size() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">                ProcessRecord r = mService.mLruProcesses.get(i);</span><br><span class="line">                <span class="keyword">if</span> (r != <span class="keyword">null</span> &amp;&amp; r.thread != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">int</span> pid = r.pid;</span><br><span class="line">                    <span class="keyword">if</span> (pid &gt; <span class="number">0</span> &amp;&amp; pid != app.pid &amp;&amp; pid != parentPid &amp;&amp; pid != MY_PID) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (r.persistent) &#123;</span><br><span class="line">                            firstPids.add(pid);</span><br><span class="line">                            <span class="keyword">if</span> (DEBUG_ANR) Slog.i(TAG, <span class="string">"Adding persistent proc: "</span> + r);</span><br><span class="line">                        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (r.treatLikeActivity) &#123;</span><br><span class="line">                            firstPids.add(pid);</span><br><span class="line">                            <span class="keyword">if</span> (DEBUG_ANR) Slog.i(TAG, <span class="string">"Adding likely IME: "</span> + r);</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            lastPids.put(pid, Boolean.TRUE);</span><br><span class="line">                            <span class="keyword">if</span> (DEBUG_ANR) Slog.i(TAG, <span class="string">"Adding ANR proc: "</span> + r);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Log the ANR to the main log. </span></span><br><span class="line">    <span class="comment">//4. 将ANR信息输出到 main log中</span></span><br><span class="line">    StringBuilder info = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">    info.setLength(<span class="number">0</span>);</span><br><span class="line">    info.append(<span class="string">"ANR in "</span>).append(app.processName);</span><br><span class="line">    <span class="keyword">if</span> (activity != <span class="keyword">null</span> &amp;&amp; activity.shortComponentName != <span class="keyword">null</span>) &#123;</span><br><span class="line">        info.append(<span class="string">" ("</span>).append(activity.shortComponentName).append(<span class="string">")"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    info.append(<span class="string">"\n"</span>);</span><br><span class="line">    info.append(<span class="string">"PID: "</span>).append(app.pid).append(<span class="string">"\n"</span>);</span><br><span class="line">    <span class="keyword">if</span> (annotation != <span class="keyword">null</span>) &#123;</span><br><span class="line">        info.append(<span class="string">"Reason: "</span>).append(annotation).append(<span class="string">"\n"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (parent != <span class="keyword">null</span> &amp;&amp; parent != activity) &#123;</span><br><span class="line">        info.append(<span class="string">"Parent: "</span>).append(parent.shortComponentName).append(<span class="string">"\n"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    ProcessCpuTracker processCpuTracker = <span class="keyword">new</span> ProcessCpuTracker(<span class="keyword">true</span>);</span><br><span class="line">    <span class="comment">// don't dump native PIDs for background ANRs unless it is the process of interest</span></span><br><span class="line">    String[] nativeProcs = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (isSilentANR) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; NATIVE_STACKS_OF_INTEREST.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) &#123;</span><br><span class="line">                nativeProcs = <span class="keyword">new</span> String[] &#123; app.processName &#125;;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        nativeProcs = NATIVE_STACKS_OF_INTEREST;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">int</span>[] pids = nativeProcs == <span class="keyword">null</span> ? <span class="keyword">null</span> : Process.getPidsForCommands(nativeProcs);</span><br><span class="line">    ArrayList&lt;Integer&gt; nativePids = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (pids != <span class="keyword">null</span>) &#123;</span><br><span class="line">        nativePids = <span class="keyword">new</span> ArrayList&lt;Integer&gt;(pids.length);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i : pids) &#123;</span><br><span class="line">            nativePids.add(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// For background ANRs, don't pass the ProcessCpuTracker to</span></span><br><span class="line">    <span class="comment">// avoid spending 1/2 second collecting stats to rank lastPids.</span></span><br><span class="line">    File tracesFile = ActivityManagerService.dumpStackTraces(</span><br><span class="line">            <span class="keyword">true</span>, firstPids,</span><br><span class="line">            (isSilentANR) ? <span class="keyword">null</span> : processCpuTracker,</span><br><span class="line">            (isSilentANR) ? <span class="keyword">null</span> : lastPids,</span><br><span class="line">            nativePids);</span><br><span class="line">    String cpuInfo = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (ActivityManagerService.MONITOR_CPU_USAGE) &#123;</span><br><span class="line">        mService.updateCpuStatsNow();</span><br><span class="line">        <span class="keyword">synchronized</span> (mService.mProcessCpuTracker) &#123;</span><br><span class="line">            cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);</span><br><span class="line">        &#125;</span><br><span class="line">        info.append(processCpuTracker.printCurrentLoad());</span><br><span class="line">        info.append(cpuInfo);</span><br><span class="line">    &#125;</span><br><span class="line">    info.append(processCpuTracker.printCurrentState(anrTime));</span><br><span class="line">    Slog.e(TAG, info.toString());<span class="comment">//将上面拿到的所有信息，打印到main log中</span></span><br><span class="line">    <span class="keyword">if</span> (tracesFile == <span class="keyword">null</span>) &#123;<span class="comment">//5.将log打印到 traces.txt中</span></span><br><span class="line">        <span class="comment">// There is no trace file, so dump (only) the alleged culprit's threads to the log</span></span><br><span class="line">        Process.sendSignal(app.pid, Process.SIGNAL_QUIT);</span><br><span class="line">    &#125;</span><br><span class="line">    mService.addErrorToDropBox(<span class="string">"anr"</span>, app, app.processName, activity, parent, annotation,</span><br><span class="line">            cpuInfo, tracesFile, <span class="keyword">null</span>);<span class="comment">// 6.将log打印到dropbox</span></span><br><span class="line">    <span class="keyword">if</span> (mService.mController != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately</span></span><br><span class="line">            <span class="keyword">int</span> res = mService.mController.appNotResponding(</span><br><span class="line">                    app.processName, app.pid, info.toString());</span><br><span class="line">            <span class="keyword">if</span> (res != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (res &lt; <span class="number">0</span> &amp;&amp; app.pid != MY_PID) &#123;</span><br><span class="line">                    app.kill(<span class="string">"anr"</span>, <span class="keyword">true</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">synchronized</span> (mService) &#123;</span><br><span class="line">                        mService.mServices.scheduleServiceTimeoutLocked(app);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">            mService.mController = <span class="keyword">null</span>;</span><br><span class="line">            Watchdog.getInstance().setActivityController(<span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 弹框处理</span></span><br><span class="line">    <span class="keyword">synchronized</span> (mService) &#123;</span><br><span class="line">        mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);</span><br><span class="line">        <span class="keyword">if</span> (isSilentANR) &#123;</span><br><span class="line">            app.kill(<span class="string">"bg anr"</span>, <span class="keyword">true</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// Set the app's notResponding state, and look up the errorReportReceiver</span></span><br><span class="line">        makeAppNotRespondingLocked(app,</span><br><span class="line">                activity != <span class="keyword">null</span> ? activity.shortComponentName : <span class="keyword">null</span>,</span><br><span class="line">                annotation != <span class="keyword">null</span> ? <span class="string">"ANR "</span> + annotation : <span class="string">"ANR"</span>,</span><br><span class="line">                info.toString());</span><br><span class="line">        <span class="comment">// Bring up the infamous App Not Responding dialog</span></span><br><span class="line">        Message msg = Message.obtain();</span><br><span class="line">        HashMap&lt;String, Object&gt; map = <span class="keyword">new</span> HashMap&lt;String, Object&gt;();</span><br><span class="line">        msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;</span><br><span class="line">        msg.obj = map;</span><br><span class="line">        msg.arg1 = aboveSystem ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">        map.put(<span class="string">"app"</span>, app);</span><br><span class="line">        <span class="keyword">if</span> (activity != <span class="keyword">null</span>) &#123;</span><br><span class="line">            map.put(<span class="string">"activity"</span>, activity);</span><br><span class="line">        &#125;</span><br><span class="line">        mService.mUiHandler.sendMessage(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>该方法的主体逻辑可以分成五个部分来看：</p><ol><li>更新CPU的统计信息。这是发生ANR时，第一次CPU使用信息的采样，采样数据会保存在mProcessStats这个变量中</li><li>将Log信息，写入Event log中</li><li>填充firstPids和lastPids数组。当前发生ANR的应用会首先被添加到firstPids中，这样打印函数栈的时候，当前进程总是在trace文件的最前面打印函数调用栈(StackTrace)。具体实现由dumpStackTraces()函数完成更新CPU的统计信息。这是发生ANR时，第二次CPU使用信息的采样，两次采样的数据分别对应ANR发生前后的CPU使用情况</li><li>准备main log需要的参数，将ANR信息输出到main log中</li><li>将log打印到trace.txt文件中</li><li>将log打印到dropbox中</li><li>弹框处理，显示ANR对话框。抛出SHOW_NOT_RESPONDING_MSG消息，AMS.MainHandler会处理这条消息，显示AppNotRespondingDialog</li></ol><p>当然，除了主体逻辑，发生ANR时还会输出各种类别的日志：</p><blockquote><p>event log，通过检索”am_anr”关键字，可以找到发生ANR的应用<br>main log，通过检索”ANR in “关键字，可以找到ANR的信息，日志的上下文会包含CPU的使用情况<br>dropbox，通过检索”anr”类型，可以找到ANR的信息,log位于/data/system/dropbox<br>traces, 发生ANR时，各进程的函数调用栈信息，log位于/data/anr<br>我们分析ANR问题，往往是从main log中的CPU使用情况和traces中的函数调用栈开始。所以，更新CPU的使用信息updateCpuStatsNow()方法和打印函数栈dumpStackTraces()方法，是系统报告ANR问题关键所在。</p></blockquote><h4 id="CPU的使用情况"><a href="#CPU的使用情况" class="headerlink" title="CPU的使用情况"></a>CPU的使用情况</h4><p>AMS.updateCpuStatsNow()方法的实现见下面的代码，更新CPU使用信息的间隔最小是5秒，即如果5秒内连续调用updateCpuStatsNow()方法，其实是没有更新CPU使用信息的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">updateCpuStatsNow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mProcessCpuTracker) &#123;</span><br><span class="line">            mProcessCpuMutexFree.set(<span class="keyword">false</span>);</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">long</span> now = SystemClock.uptimeMillis();</span><br><span class="line">            <span class="keyword">boolean</span> haveNewCpuStats = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (MONITOR_CPU_USAGE &amp;&amp;</span><br><span class="line">                    mLastCpuTime.get() &lt; (now-MONITOR_CPU_MIN_TIME)) &#123;</span><br><span class="line">                mLastCpuTime.set(now);</span><br><span class="line">                mProcessCpuTracker.update();</span><br><span class="line">                <span class="keyword">if</span> (mProcessCpuTracker.hasGoodLastStats()) &#123;</span><br><span class="line">                    haveNewCpuStats = <span class="keyword">true</span>;</span><br><span class="line">                    <span class="comment">//Slog.i(TAG, mProcessCpu.printCurrentState());</span></span><br><span class="line">                    <span class="comment">//Slog.i(TAG, "Total CPU usage: "</span></span><br><span class="line">                    <span class="comment">//        + mProcessCpu.getTotalCpuPercent() + "%");</span></span><br><span class="line"></span><br><span class="line">                    <span class="comment">// Slog the cpu usage if the property is set.</span></span><br><span class="line">                    <span class="keyword">if</span> (<span class="string">"true"</span>.equals(SystemProperties.get(<span class="string">"events.cpu"</span>))) &#123;</span><br><span class="line">                        <span class="keyword">int</span> user = mProcessCpuTracker.getLastUserTime();</span><br><span class="line">                        <span class="keyword">int</span> system = mProcessCpuTracker.getLastSystemTime();</span><br><span class="line">                        <span class="keyword">int</span> iowait = mProcessCpuTracker.getLastIoWaitTime();</span><br><span class="line">                        <span class="keyword">int</span> irq = mProcessCpuTracker.getLastIrqTime();</span><br><span class="line">                        <span class="keyword">int</span> softIrq = mProcessCpuTracker.getLastSoftIrqTime();</span><br><span class="line">                        <span class="keyword">int</span> idle = mProcessCpuTracker.getLastIdleTime();</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">int</span> total = user + system + iowait + irq + softIrq + idle;</span><br><span class="line">                        <span class="keyword">if</span> (total == <span class="number">0</span>) total = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">                        EventLog.writeEvent(EventLogTags.CPU,</span><br><span class="line">                                ((user+system+iowait+irq+softIrq) * <span class="number">100</span>) / total,</span><br><span class="line">                                (user * <span class="number">100</span>) / total,</span><br><span class="line">                                (system * <span class="number">100</span>) / total,</span><br><span class="line">                                (iowait * <span class="number">100</span>) / total,</span><br><span class="line">                                (irq * <span class="number">100</span>) / total,</span><br><span class="line">                                (softIrq * <span class="number">100</span>) / total);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">final</span> BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();</span><br><span class="line">            <span class="keyword">synchronized</span>(bstats) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span>(mPidsSelfLocked) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (haveNewCpuStats) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (bstats.startAddingCpuLocked()) &#123;</span><br><span class="line">                            <span class="keyword">int</span> totalUTime = <span class="number">0</span>;</span><br><span class="line">                            <span class="keyword">int</span> totalSTime = <span class="number">0</span>;</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> N = mProcessCpuTracker.countStats();</span><br><span class="line">                            <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;N; i++) &#123;</span><br><span class="line">                                ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);</span><br><span class="line">                                <span class="keyword">if</span> (!st.working) &#123;</span><br><span class="line">                                    <span class="keyword">continue</span>;</span><br><span class="line">                                &#125;</span><br><span class="line">                                ProcessRecord pr = mPidsSelfLocked.get(st.pid);</span><br><span class="line">                                totalUTime += st.rel_utime;</span><br><span class="line">                                totalSTime += st.rel_stime;</span><br><span class="line">                                <span class="keyword">if</span> (pr != <span class="keyword">null</span>) &#123;</span><br><span class="line">                                    BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;</span><br><span class="line">                                    <span class="keyword">if</span> (ps == <span class="keyword">null</span> || !ps.isActive()) &#123;</span><br><span class="line">                                        pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(</span><br><span class="line">                                                pr.info.uid, pr.processName);</span><br><span class="line">                                    &#125;</span><br><span class="line">                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);</span><br><span class="line">                                    pr.curCpuTime += st.rel_utime + st.rel_stime;</span><br><span class="line">                                    <span class="keyword">if</span> (pr.lastCpuTime == <span class="number">0</span>) &#123;</span><br><span class="line">                                        pr.lastCpuTime = pr.curCpuTime;</span><br><span class="line">                                    &#125;</span><br><span class="line">                                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                                    BatteryStatsImpl.Uid.Proc ps = st.batteryStats;</span><br><span class="line">                                    <span class="keyword">if</span> (ps == <span class="keyword">null</span> || !ps.isActive()) &#123;</span><br><span class="line">                                        st.batteryStats = ps = bstats.getProcessStatsLocked(</span><br><span class="line">                                                bstats.mapUid(st.uid), st.name);</span><br><span class="line">                                    &#125;</span><br><span class="line">                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);</span><br><span class="line">                                &#125;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> userTime = mProcessCpuTracker.getLastUserTime();</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> systemTime = mProcessCpuTracker.getLastSystemTime();</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> iowaitTime = mProcessCpuTracker.getLastIoWaitTime();</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> irqTime = mProcessCpuTracker.getLastIrqTime();</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();</span><br><span class="line">                            <span class="keyword">final</span> <span class="keyword">int</span> idleTime = mProcessCpuTracker.getLastIdleTime();</span><br><span class="line">                            bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,</span><br><span class="line">                                    systemTime, iowaitTime, irqTime, softIrqTime, idleTime);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (mLastWriteTime &lt; (now-BATTERY_STATS_TIME)) &#123;</span><br><span class="line">                    mLastWriteTime = now;</span><br><span class="line">                    mBatteryStatsService.scheduleWriteToDisk();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>CPU使用信息由ProcessCpuTracker这个类维护， 每次调用ProcessCpuTracker.update()方法，就会读取设备节点 /proc 下的文件，来更新CPU使用信息，具体有以下几个维度：</p><blockquote><p>CPU的使用时间: 读取 /proc/stat</p><blockquote><p>user： 用户进程的CPU使用时间<br>nice： 降低过优先级进程的CPU使用时间。Linux进程都有优先级，这个优先级可以进行动态调整，譬如进程初始优先级的值设为10,运行时降低为8,那么，修正值-2就定义为nice。 Android将user和nice这两个时间归类成user<br>sys： 内核进程的CPU使用时间<br>idle： CPU空闲的时间<br>wait： CPU等待IO的时间<br>hw irq： 硬件中断的时间。如果外设（譬如硬盘）出现故障，需要通过硬件终端通知CPU保存现场，发生上下文切换的时间就是CPU的硬件中断时间<br>sw irg： 软件中断的时间。同硬件中断一样，如果软件要求CPU中断，则上下文切换的时间就是CPU的软件中断时间</p></blockquote></blockquote><blockquote><p>CPU负载: 读取 /proc/loadavg, 统计最近1分钟，5分钟，15分钟内，CPU的平均活动进程数。 CPU的负载可以比喻成超市收银员负载，如果有1个人正在买单，有2个人在排队，那么该收银员的负载就是3。 在收银员工作时，不断会有人买单完成，也不断会有人排队，可以在固定的时间间隔内(譬如，每隔5秒)统计一次负载，那么，就可以统计出一段时间内的平均负载。</p></blockquote><blockquote><p>页错误信息： 进程的CPU使用率最后输出的“faults: xxx minor/major”部分表示的是页错误次数，当次数为0时不显示。 major是指Major Page Fault(主要页错误，简称MPF)，内核在读取数据时会先后查找CPU的高速缓存和物理内存，如果找不到会发出一个MPF信息，请求将数据加载到内存。 minor是指Minor Page Fault(次要页错误，简称MnPF)，磁盘数据被加载到内存后，内核再次读取时，会发出一个MnPF信息。 一个文件第一次被读写时会有很多的MPF，被缓存到内存后再次访问MPF就会很少，MnPF反而变多，这是内核为减少效率低下的磁盘I/O操作采用的缓存技术的结果。</p></blockquote><h4 id="堆栈调用"><a href="#堆栈调用" class="headerlink" title="堆栈调用"></a>堆栈调用</h4><p>AMS.dumpStackTraces()方法用于打印进程的函数调用栈，该方法的主体逻辑如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * If a stack trace dump file is configured, dump process stack traces.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> clearTraces causes the dump file to be erased prior to the new</span></span><br><span class="line"><span class="comment"> *    traces being written, if true; when false, the new traces will be</span></span><br><span class="line"><span class="comment"> *    appended to any existing file content.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> firstPids of dalvik VM processes to dump stack traces for first</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> lastPids of dalvik VM processes to dump stack traces for last</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> nativePids optional list of native pids to dump stack crawls</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> File <span class="title">dumpStackTraces</span><span class="params">(<span class="keyword">boolean</span> clearTraces, ArrayList&lt;Integer&gt; firstPids,</span></span></span><br><span class="line"><span class="function"><span class="params">        ProcessCpuTracker processCpuTracker, SparseArray&lt;Boolean&gt; lastPids,</span></span></span><br><span class="line"><span class="function"><span class="params">        ArrayList&lt;Integer&gt; nativePids)</span> </span>&#123;</span><br><span class="line">    ArrayList&lt;Integer&gt; extraPids = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Measure CPU usage as soon as we're called in order to get a realistic sampling</span></span><br><span class="line">    <span class="comment">// of the top users at the time of the request.</span></span><br><span class="line">    <span class="keyword">if</span> (processCpuTracker != <span class="keyword">null</span>) &#123;</span><br><span class="line">        processCpuTracker.init();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">200</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException ignored) &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        processCpuTracker.update();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// We'll take the stack crawls of just the top apps using CPU.</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> N = processCpuTracker.countWorkingStats();</span><br><span class="line">        extraPids = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; N &amp;&amp; extraPids.size() &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);</span><br><span class="line">            <span class="keyword">if</span> (lastPids.indexOfKey(stats.pid) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) Slog.d(TAG, <span class="string">"Collecting stacks for extra pid "</span> + stats.pid);</span><br><span class="line"></span><br><span class="line">                extraPids.add(stats.pid);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (DEBUG_ANR) &#123;</span><br><span class="line">                Slog.d(TAG, <span class="string">"Skipping next CPU consuming process, not a java proc: "</span></span><br><span class="line">                        + stats.pid);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">boolean</span> useTombstonedForJavaTraces = <span class="keyword">false</span>;</span><br><span class="line">    File tracesFile;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> String tracesDirProp = SystemProperties.get(<span class="string">"dalvik.vm.stack-trace-dir"</span>, <span class="string">""</span>);</span><br><span class="line">    <span class="keyword">if</span> (tracesDirProp.isEmpty()) &#123;</span><br><span class="line">        <span class="comment">// When dalvik.vm.stack-trace-dir is not set, we are using the "old" trace</span></span><br><span class="line">        <span class="comment">// dumping scheme. All traces are written to a global trace file (usually</span></span><br><span class="line">        <span class="comment">// "/data/anr/traces.txt") so the code below must take care to unlink and recreate</span></span><br><span class="line">        <span class="comment">// the file if requested.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// This mode of operation will be removed in the near future.</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        String globalTracesPath = SystemProperties.get(<span class="string">"dalvik.vm.stack-trace-file"</span>, <span class="keyword">null</span>);</span><br><span class="line">        <span class="keyword">if</span> (globalTracesPath.isEmpty()) &#123;</span><br><span class="line">            Slog.w(TAG, <span class="string">"dumpStackTraces: no trace path configured"</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        tracesFile = <span class="keyword">new</span> File(globalTracesPath);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (clearTraces &amp;&amp; tracesFile.exists()) &#123;</span><br><span class="line">                tracesFile.delete();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            tracesFile.createNewFile();</span><br><span class="line">            FileUtils.setPermissions(globalTracesPath, <span class="number">0666</span>, -<span class="number">1</span>, -<span class="number">1</span>); <span class="comment">// -rw-rw-rw-</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            Slog.w(TAG, <span class="string">"Unable to prepare ANR traces file: "</span> + tracesFile, e);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        File tracesDir = <span class="keyword">new</span> File(tracesDirProp);</span><br><span class="line">        <span class="comment">// When dalvik.vm.stack-trace-dir is set, we use the "new" trace dumping scheme.</span></span><br><span class="line">        <span class="comment">// Each set of ANR traces is written to a separate file and dumpstate will process</span></span><br><span class="line">        <span class="comment">// all such files and add them to a captured bug report if they're recent enough.</span></span><br><span class="line">        maybePruneOldTraces(tracesDir);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// <span class="doctag">NOTE:</span> We should consider creating the file in native code atomically once we've</span></span><br><span class="line">        <span class="comment">// gotten rid of the old scheme of dumping and lot of the code that deals with paths</span></span><br><span class="line">        <span class="comment">// can be removed.</span></span><br><span class="line">        tracesFile = createAnrDumpFile(tracesDir);</span><br><span class="line">        <span class="keyword">if</span> (tracesFile == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        useTombstonedForJavaTraces = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,</span><br><span class="line">            useTombstonedForJavaTraces);</span><br><span class="line">    <span class="keyword">return</span> tracesFile;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">dumpStackTraces</span><span class="params">(String tracesFile, ArrayList&lt;Integer&gt; firstPids,</span></span></span><br><span class="line"><span class="function"><span class="params">        ArrayList&lt;Integer&gt; nativePids, ArrayList&lt;Integer&gt; extraPids,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">boolean</span> useTombstonedForJavaTraces)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// We don't need any sort of inotify based monitoring when we're dumping traces via</span></span><br><span class="line">    <span class="comment">// tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full</span></span><br><span class="line">    <span class="comment">// control of all writes to the file in question.</span></span><br><span class="line">    <span class="keyword">final</span> DumpStackFileObserver observer;</span><br><span class="line">    <span class="keyword">if</span> (useTombstonedForJavaTraces) &#123;</span><br><span class="line">        observer = <span class="keyword">null</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// Use a FileObserver to detect when traces finish writing.</span></span><br><span class="line">        <span class="comment">// The order of traces is considered important to maintain for legibility.</span></span><br><span class="line">        observer = <span class="keyword">new</span> DumpStackFileObserver(tracesFile);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// We must complete all stack dumps within 20 seconds.</span></span><br><span class="line">    <span class="keyword">long</span> remainingTime = <span class="number">20</span> * <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (observer != <span class="keyword">null</span>) &#123;</span><br><span class="line">            observer.startWatching();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// First collect all of the stacks of the most important pids.</span></span><br><span class="line">        <span class="keyword">if</span> (firstPids != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">int</span> num = firstPids.size();</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) Slog.d(TAG, <span class="string">"Collecting stacks for pid "</span></span><br><span class="line">                        + firstPids.get(i));</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">long</span> timeTaken;</span><br><span class="line">                <span class="keyword">if</span> (useTombstonedForJavaTraces) &#123;</span><br><span class="line">                    timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                remainingTime -= timeTaken;</span><br><span class="line">                <span class="keyword">if</span> (remainingTime &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    Slog.e(TAG, <span class="string">"Aborting stack trace dump (current firstPid="</span> + firstPids.get(i) +</span><br><span class="line">                        <span class="string">"); deadline exceeded."</span>);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) &#123;</span><br><span class="line">                    Slog.d(TAG, <span class="string">"Done with pid "</span> + firstPids.get(i) + <span class="string">" in "</span> + timeTaken + <span class="string">"ms"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Next collect the stacks of the native pids</span></span><br><span class="line">        <span class="keyword">if</span> (nativePids != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> pid : nativePids) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) Slog.d(TAG, <span class="string">"Collecting stacks for native pid "</span> + pid);</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">long</span> nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">long</span> start = SystemClock.elapsedRealtime();</span><br><span class="line">                Debug.dumpNativeBacktraceToFileTimeout(</span><br><span class="line">                        pid, tracesFile, (<span class="keyword">int</span>) (nativeDumpTimeoutMs / <span class="number">1000</span>));</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">long</span> timeTaken = SystemClock.elapsedRealtime() - start;</span><br><span class="line"></span><br><span class="line">                remainingTime -= timeTaken;</span><br><span class="line">                <span class="keyword">if</span> (remainingTime &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    Slog.e(TAG, <span class="string">"Aborting stack trace dump (current native pid="</span> + pid +</span><br><span class="line">                        <span class="string">"); deadline exceeded."</span>);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) &#123;</span><br><span class="line">                    Slog.d(TAG, <span class="string">"Done with native pid "</span> + pid + <span class="string">" in "</span> + timeTaken + <span class="string">"ms"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Lastly, dump stacks for all extra PIDs from the CPU tracker.</span></span><br><span class="line">        <span class="keyword">if</span> (extraPids != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> pid : extraPids) &#123;</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) Slog.d(TAG, <span class="string">"Collecting stacks for extra pid "</span> + pid);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">long</span> timeTaken;</span><br><span class="line">                <span class="keyword">if</span> (useTombstonedForJavaTraces) &#123;</span><br><span class="line">                    timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    timeTaken = observer.dumpWithTimeout(pid, remainingTime);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                remainingTime -= timeTaken;</span><br><span class="line">                <span class="keyword">if</span> (remainingTime &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    Slog.e(TAG, <span class="string">"Aborting stack trace dump (current extra pid="</span> + pid +</span><br><span class="line">                            <span class="string">"); deadline exceeded."</span>);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_ANR) &#123;</span><br><span class="line">                    Slog.d(TAG, <span class="string">"Done with extra pid "</span> + pid + <span class="string">" in "</span> + timeTaken + <span class="string">"ms"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (observer != <span class="keyword">null</span>) &#123;</span><br><span class="line">            observer.stopWatching();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="问题分析方法"><a href="#问题分析方法" class="headerlink" title="问题分析方法"></a>问题分析方法</h2><p>分析ANR问题，有三大利器：Logcat，traces和StrictMode。 在StrictMode机制一文中，有介绍StrictMode的实现机制以及用途，本文中不讨论利用StrictMode来解决ANR问题，但各位读者需要有这个意识。 在Watchdog机制以及问题分析一文中，我们介绍过logcat和traces这两种日志的用途。 分析ANR问题同Watchdog问题一样，都需要经过日志获取、问题定位和场景还原三个步骤。</p><h3 id="日志的获取"><a href="#日志的获取" class="headerlink" title="日志的获取"></a>日志的获取</h3><p>分析ANR问题最主要的途径就是通过log来分析。Android中log的获取见，Android的log机制分析一问中log的存储位置章节。</p><h3 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h3><p>通过在event log中检索 am_anr 关键字，就可以找到发生ANR的进程，譬如以下日志：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">48</span>:<span class="number">27</span> <span class="number">820</span> <span class="number">907</span> I am_anr: [<span class="number">0</span>,<span class="number">29533</span>,com.android.systemui,<span class="number">1082670605</span>,Broadcast of Intent &#123; act=android.intent.action.TIME_TICK flg=<span class="number">0x50000114</span> (has extras) &#125;]</span><br></pre></td></tr></table></figure></p><p>表示在 10-16 00:48:27 这个时刻，PID为 29533 进程发生了ANR，进程名是 com.android.systemui<br>接下来可以在system log检索 ANR in 关键字，找到发生ANR前后的CPU使用情况：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: ANR in com.android.systemui, time=<span class="number">130090695</span></span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: Reason: Broadcast of Intent &#123; act=android.intent.action.TIME_TICK flg=<span class="number">0x50000114</span> (has extras) &#125;</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: Load: <span class="number">30.4</span> / <span class="number">22.34</span> / <span class="number">19.94</span></span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: Android time :[<span class="number">2015</span>-<span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">05.76</span>] [<span class="number">130191</span>,<span class="number">266</span>]</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: CPU usage from <span class="number">6753</span>ms to -<span class="number">4</span>ms ago:</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">47</span>% <span class="number">320</span>/netd: <span class="number">3.1</span>% user + <span class="number">44</span>% kernel / faults: <span class="number">14886</span> minor <span class="number">3</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">15</span>% <span class="number">10007</span>/com.sohu.sohuvideo: <span class="number">2.8</span>% user + <span class="number">12</span>% kernel / faults: <span class="number">1144</span> minor</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">13</span>% <span class="number">10654</span>/hif_thread: <span class="number">0</span>% user + <span class="number">13</span>% kernel</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">11</span>% <span class="number">175</span>/mmcqd/<span class="number">0</span>: <span class="number">0</span>% user + <span class="number">11</span>% kernel</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">5.1</span>% <span class="number">12165</span>/app_process: <span class="number">1.6</span>% user + <span class="number">3.5</span>% kernel / faults: <span class="number">9703</span> minor <span class="number">540</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">3.3</span>% <span class="number">29533</span>/com.android.systemui: <span class="number">2.6</span>% user + <span class="number">0.7</span>% kernel / faults: <span class="number">8402</span> minor <span class="number">343</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">3.2</span>% <span class="number">820</span>/system_server: <span class="number">0.8</span>% user + <span class="number">2.3</span>% kernel / faults: <span class="number">5120</span> minor <span class="number">523</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">2.5</span>% <span class="number">11817</span>/com.netease.pomelo.push.l.messageservice_V2: <span class="number">0.7</span>% user + <span class="number">1.7</span>% kernel / faults: <span class="number">7728</span> minor <span class="number">687</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">1.6</span>% <span class="number">11887</span>/com.android.email: <span class="number">0.5</span>% user + <span class="number">1</span>% kernel / faults: <span class="number">6259</span> minor <span class="number">587</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">1.4</span>% <span class="number">11854</span>/com.android.settings: <span class="number">0.7</span>% user + <span class="number">0.7</span>% kernel / faults: <span class="number">5404</span> minor <span class="number">471</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">1.4</span>% <span class="number">11869</span>/android.process.acore: <span class="number">0.7</span>% user + <span class="number">0.7</span>% kernel / faults: <span class="number">6131</span> minor <span class="number">561</span> major</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:   <span class="number">1.3</span>% <span class="number">11860</span>/com.tencent.mobileqq: <span class="number">0.1</span>% user + <span class="number">1.1</span>% kernel / faults: <span class="number">5542</span> minor <span class="number">470</span> major</span><br><span class="line">...</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:  +<span class="number">0</span>% <span class="number">12832</span>/cat: <span class="number">0</span>% user + <span class="number">0</span>% kernel</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager:  +<span class="number">0</span>% <span class="number">13211</span>/zygote64: <span class="number">0</span>% user + <span class="number">0</span>% kernel</span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">50</span>:<span class="number">10</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: <span class="number">87</span>% TOTAL: <span class="number">3</span>% user + <span class="number">18</span>% kernel + <span class="number">64</span>% iowait + <span class="number">0.5</span>% softirq</span><br></pre></td></tr></table></figure></p><p>这一段日志对于Android开发人员而言，实在太熟悉不过了，它包含的信息量巨大：</p><p>• <strong>发生ANR的时间。</strong>event log中，ANR的时间是 00：48：27，因为AMS.appNotResponding()首先会打印event log，然后再打印system log， 所以，在system log中，找到ANR的时间是 00:50:10。可以从这个时间点之前的日志中，还原ANR出现时系统的运行状态<br>• <strong>打印ANR日志的进程。</strong>ANR日志都是在system_server进程的AMS线程打印的，在event log和system log中，都能看到 820 和 907， 所以system_server的PID是 802，AMS线程的TID是 907。ANR的监测机制实现在AMS线程，分析一些受系统影响的ANR，需要知道system_server进程的运行状态<br>• <strong>发生ANR的进程。</strong>ANR in关键字就表明了当前ANR的进程是com.android.system.ui，通过event log，知道进程的PID是 29533<br>• <strong>发生ANR的原因。</strong>Reason关键字表明了当前发生ANR的原因是，处理TIME_TICK广播消息超时。 隐含的意思是TIME_TICK是一个串行广播消息，在 29533 的主线程中，执行BroadcastReceiver.onReceive()方法已经超过10秒<br>• <strong>CPU负载。</strong>Load关键字表明了最近1分钟、5分钟、15分钟内的CPU负载分别是30.4、22.3、19.94。CPU最近1分钟的负载最具参考价值，因为ANR的超时限制基本都是1分钟以内， 这可以近似的理解为CPU最近1分钟平均有30.4个任务要处理，这个负载值是比较高的<br>• <strong>CPU使用统计时间段。</strong>CPU usage from XX to XX ago关键字表明了这是在ANR发生之前一段时间内的CPU统计。 类似的还有CPU usage from XX to XX after关键字，表明是ANR发生之后一段时间内的CPU统计<br>• <strong>各进程的CPU使用率。</strong>我们以com.android.systemui进程的CPU使用率为例，它包含以下信息：<br>    1）<strong>总的CPU使用率:</strong> 3.3%，其中systemui进程在用户态的CPU使用率是2.6%，在内核态的使用率是0.7%<br>    2) <strong>缺页次数fault：</strong>8402 minor表示高速缓存中的缺页次数，343 major表示内存的缺页次数。minor可以理解为进程在做内存访问，major可以理解为进程在做IO操作。 当前minor和major值都是比较高的，从侧面反映了发生ANR之前，systemui进程有有较多的内存访问操作，引发的IO次数也会较多<br>    3) <strong>CPU使用率前面的 “+”。</strong>部分进程的CPU使用率前面有 “+” 号，譬如cat和zygote64，表示在上一次CPU统计的时间片段内，还没有这些进程，而这一次CPU统计的时间片段内，运行了这些进程。 类似的还有 “-” 号，表示两次CPU统计时间片段时，这些进程消亡了<br>• <strong>CPU使用汇总。</strong>TOTAL关键字表明了CPU使用的汇总，87%是总的CPU使用率，其中有一项iowait表明CPU在等待IO的时间，占到64%，说明发生ANR以前，有大量的IO操作。app_process、 system_server, com.android.systemui这几个进程的major值都比较大，说明这些进程的IO操作较为频繁，从而拉升了整个iowait的时间</p><p>信息量是如此的庞大，以致于我们都要下结论了：CPU大量的时间都在等待IO，导致systemui进程分配不到CPU时间，从而主线程处理广播消息超时，发生了ANR。<br>对于一个严谨的开发人员而言，这种结论下得有点早，因为还有太多的疑问：</p><ul><li>systemui进程也分到了一些CPU时间(3.3%)，难道BroadcastReceiver.onReceive()方法就一直无法执行吗？</li><li>为什么iowait的时间会这么多，而且多个进程的major值都很高？<br>接下来还是需要从其他日志中还原ANR出现的场景。</li></ul><h3 id="场景还原"><a href="#场景还原" class="headerlink" title="场景还原"></a>场景还原</h3><h4 id="第一个假设和验证"><a href="#第一个假设和验证" class="headerlink" title="第一个假设和验证"></a>第一个假设和验证</h4><p>带着上文提出来的第一个疑问，我们先来做一个假设：如果systemui进程正在执行BroadcatReceiver.onReceive()方法，那么从traces.txt文件中，应该可以看到主线程的函数调用栈正在执行这个方法。<br>接下来，我们首先从traces文件中，找到发生ANR时(00:48:27)，sysemtui进程的函数调用栈信息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line">----- pid <span class="number">29533</span> at <span class="number">2015</span>-<span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">48</span>:<span class="number">06</span> -----</span><br><span class="line">Cmd line: com.android.systemui</span><br><span class="line"></span><br><span class="line"><span class="function">DALVIK <span class="title">THREADS</span> <span class="params">(<span class="number">53</span>)</span>:</span></span><br><span class="line"><span class="function">"main" prio</span>=<span class="number">5</span> tid=<span class="number">1</span> Native</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x75bd5818</span> self=<span class="number">0x7f8549a000</span></span><br><span class="line">  | sysTid=<span class="number">29533</span> nice=<span class="number">0</span> cgrp=bg_non_interactive sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f894bbe58</span></span><br><span class="line">  | state=S schedstat=( <span class="number">288625433917</span> <span class="number">93454573244</span> <span class="number">903419</span> ) utm=<span class="number">20570</span> stm=<span class="number">8292</span> core=<span class="number">3</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7fdffda000</span>-<span class="number">0x7fdffdc000</span> stackSize=<span class="number">8</span>MB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  native: #00 pc 00060b0c  /system/lib64/libc.so (__epoll_pwait+8)</span><br><span class="line">  native: #01 pc 0001bb54  /system/lib64/libc.so (epoll_pwait+32)</span><br><span class="line">  native: #02 pc 0001b3d8  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)</span><br><span class="line">  native: #03 pc 0001b75c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+76)</span><br><span class="line">  native: #04 pc 000d7194  /system/lib64/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+48)</span><br><span class="line">  at android.os.MessageQueue.nativePollOnce(Native method)</span><br><span class="line">  at android.os.MessageQueue.next(MessageQueue.java:<span class="number">148</span>)</span><br><span class="line">  at android.os.Looper.loop(Looper.java:<span class="number">151</span>)</span><br><span class="line">  at android.app.ActivityThread.main(ActivityThread.java:<span class="number">5718</span>)</span><br><span class="line">  at java.lang.reflect.Method.invoke!(Native method)</span><br><span class="line">  at java.lang.reflect.Method.invoke(Method.java:<span class="number">372</span>)</span><br><span class="line">  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:<span class="number">975</span>)</span><br><span class="line">  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">770</span>)</span><br><span class="line"></span><br><span class="line">----- pid <span class="number">29533</span> at <span class="number">2015</span>-<span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">48</span>:<span class="number">29</span> -----</span><br><span class="line">Cmd line: com.android.systemui</span><br><span class="line"></span><br><span class="line"><span class="function">DALVIK <span class="title">THREADS</span> <span class="params">(<span class="number">54</span>)</span>:</span></span><br><span class="line"><span class="function">"main" prio</span>=<span class="number">5</span> tid=<span class="number">1</span> Blocked</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x75bd5818</span> self=<span class="number">0x7f8549a000</span></span><br><span class="line">  | sysTid=<span class="number">29533</span> nice=<span class="number">0</span> cgrp=bg_non_interactive sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f894bbe58</span></span><br><span class="line">  | state=S schedstat=( <span class="number">289080040422</span> <span class="number">93461978317</span> <span class="number">904874</span> ) utm=<span class="number">20599</span> stm=<span class="number">8309</span> core=<span class="number">0</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7fdffda000</span>-<span class="number">0x7fdffdc000</span> stackSize=<span class="number">8</span>MB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  at com.mediatek.anrappmanager.MessageLogger.println(SourceFile:<span class="number">77</span>)</span><br><span class="line">  - waiting to lock &lt;<span class="number">0x26b337a3</span>&gt; (a com.mediatek.anrappmanager.MessageLogger) held by thread <span class="number">49</span></span><br><span class="line">  at android.os.Looper.loop(Looper.java:<span class="number">195</span>)</span><br><span class="line">  at android.app.ActivityThread.main(ActivityThread.java:<span class="number">5718</span>)</span><br><span class="line">  at java.lang.reflect.Method.invoke!(Native method)</span><br><span class="line">  at java.lang.reflect.Method.invoke(Method.java:<span class="number">372</span>)</span><br><span class="line">  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:<span class="number">975</span>)</span><br><span class="line">  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">770</span>)</span><br><span class="line">...</span><br><span class="line"><span class="string">"Binder_5"</span> prio=<span class="number">5</span> tid=<span class="number">49</span> Native</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x136760a0</span> self=<span class="number">0x7f7e453000</span></span><br><span class="line">  | sysTid=<span class="number">6945</span> nice=<span class="number">0</span> cgrp=<span class="keyword">default</span> sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f6e3ce000</span></span><br><span class="line">  | state=S schedstat=( <span class="number">5505571091</span> <span class="number">4567508913</span> <span class="number">30743</span> ) utm=<span class="number">264</span> stm=<span class="number">286</span> core=<span class="number">4</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7f6b83f000</span>-<span class="number">0x7f6b841000</span> stackSize=<span class="number">1008</span>KB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  native: #00 pc 00019d14  /system/lib64/libc.so (syscall+28)</span><br><span class="line">  native: #01 pc 0005b5d8  /system/lib64/libaoc.so (???)</span><br><span class="line">  native: #02 pc 002c6f18  /system/lib64/libaoc.so (???)</span><br><span class="line">  native: #03 pc 00032c40  /system/lib64/libaoc.so (???)</span><br><span class="line">  at libcore.io.Posix.getpid(Native method)</span><br><span class="line">  at libcore.io.ForwardingOs.getpid(ForwardingOs.java:<span class="number">83</span>)</span><br><span class="line">  at android.system.Os.getpid(Os.java:<span class="number">176</span>)</span><br><span class="line">  at android.os.Process.myPid(Process.java:<span class="number">754</span>)</span><br><span class="line">  at com.mediatek.anrappmanager.MessageLogger.dump(SourceFile:<span class="number">219</span>)</span><br><span class="line">  - locked &lt;<span class="number">0x26b337a3</span>&gt; (a com.mediatek.anrappmanager.MessageLogger)</span><br><span class="line">  at com.mediatek.anrappmanager.ANRAppManager.dumpMessageHistory(SourceFile:<span class="number">65</span>)</span><br><span class="line">  at android.app.ActivityThread$ApplicationThread.dumpMessageHistory(ActivityThread.java:<span class="number">1302</span>)</span><br><span class="line">  at android.app.ApplicationThreadNative.onTransact(ApplicationThreadNative.java:<span class="number">682</span>)</span><br><span class="line">  at android.os.Binder.execTransact(Binder.java:<span class="number">451</span>)</span><br></pre></td></tr></table></figure><p>最终，我们找到systemui进程ANR时刻(00:48:27)附近的两个函数调用栈:</p><ul><li>在ANR发生之前(00:48:06)，主线程的函数调用栈处于正常状态：消息队列中，循环中处理消息</li><li>在ANR发生之后2秒(00:48:29)，主线程处于Blocked状态，在等待一个被49号线程持有的锁。而49号线程是一个Binder线程，anrappmanager正在做dump操作。</li></ul><blockquote><p>笔者分析的日志是MTK平台产生的，所以从函数调用栈中看到com.mediatek.anrappmanager.MessageLogger这样的类，它是MTK在AOSP上的扩展，用于打印ANR日志。</p></blockquote><p>至此，systemui进程发生ANR的直接原因我们已经找到了，systemui进程正在打印traces，存在较长时间的IO操作，导致主线程阻塞，从而无法处理TIME_TICK广播消息，所以发生了ANR。</p><p>要避免这种场景下的ANR，我们就需要打破主线程中Blocked的逻辑。其实本例是由于MTK在AOSP的android.os.Looper.loop()扩展了打印消息队列的功能，该功能存在设计缺陷，会导致锁等待的情况。</p><h4 id="第二个假设和验证"><a href="#第二个假设和验证" class="headerlink" title="第二个假设和验证"></a>第二个假设和验证</h4><p>我们进一步挖掘在systemui还没有发生ANR时，就在打印traces的原因。带着上文提出的第二个疑问，我们来做另一个假设： iowait较高，而且多个进程的major都很高，可能是由于当前正在调用AMS.dumpStackTraces()方法，很多进程都需要将自己的函数调用栈写到traces文件，所以IO就会较高。 如果当前正在调用AMS.dumpStackTraces()方法，那说明当时系统已经发生了异常，要么已经有ANR发生，要么有SNR发生</p><p>从event log中，我们检索到了另一个ANR：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">47</span>:<span class="number">58</span> <span class="number">820</span> <span class="number">907</span> I am_anr  : [<span class="number">0</span>,<span class="number">10464</span>,com.android.settings,<span class="number">1086864965</span>,<span class="function">Input dispatching timed <span class="title">out</span> <span class="params">(Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.  Outbound queue length: <span class="number">0.</span>  Wait queue length: <span class="number">1.</span>)</span>]</span></span><br></pre></td></tr></table></figure></p><p>在 00:47:58 这个时刻，com.android.settings进程发生了ANR，而且ANR的时间在systemui之前(00:48:27)。这一下，我们就找到佐证了，正是因为settings进程先发生了ANR，调用AMS.dumpStackTraces()， 从而很多进程都开始了打印traces的操作，所以系统的整个iowait比较高，大量进程的major值也比较高，systemui就在其列。在MTK逻辑的影响下，打印ANR日志会导致主线程阻塞，从而就连带引发了其他应用的ANR。</p><p>在system log中，我们检索到了settings进程ANR的CPU使用信息：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">48</span>:<span class="number">12</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: ANR in com.android.settings (com.android.settings/.SubSettings), time=<span class="number">130063718</span></span><br><span class="line"><span class="number">10</span>-<span class="number">16</span> <span class="number">00</span>:<span class="number">48</span>:<span class="number">12</span> <span class="number">820</span> <span class="number">907</span> E ActivityManager: Reason: <span class="function">Input dispatching timed <span class="title">out</span> <span class="params">(Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.  Outbound queue length: <span class="number">0.</span>  Wait queue length: <span class="number">1.</span>)</span></span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager: Load: 21.37 / 19.25 / 18.84</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager: Android time :[2015-10-16 00:48:12.24] [130077,742]</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager: CPU usage from 0ms to 7676ms later:</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager:   91% 820/system_server: 16% user + 75% kernel / faults: 13192 minor 167 major</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager:   3.2% 175/mmcqd/0: 0% user + 3.2% kernel</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager:   2.9% 29533/com.android.systemui: 2.3% user + 0.6% kernel / faults: 1352 minor 10 major</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager:   2.2% 1736/com.android.phone: 0.9% user + 1.3% kernel / faults: 1225 minor 1 major</span></span><br><span class="line"><span class="function">10-16 00:48:12 820 907 E ActivityManager:   2.2% 10464/com.android.settings: 0.7% user + 1.4% kernel / faults: 2801 minor 105 major</span></span><br></pre></td></tr></table></figure></p><p>具体的涵义我们不再赘述了，只关注一下ANR的原因:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Input dispatching timed <span class="title">out</span> <span class="params">(Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it.</span></span></span><br><span class="line"><span class="function"><span class="params">Outbound queue length: <span class="number">0.</span> Wait queue length: <span class="number">1.</span>)</span></span></span><br></pre></td></tr></table></figure></p><p>之前对Input ANR机制的分析派上用长了，我们轻松知道这种ANR的原因是什么。 Wait queue length： 1表示之前的输入事件已经派发到Settings进程了，但Settings进程还没有处理完毕，新来的KeyEvent事件已经等待超过了5秒，所以ANR产生了。<br>接下来，又需要找到Settings的traces，分析Settings主线程处理输入事件超时的原因，我们点到为止。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文对Android ANR机制进行了深入的分析：<br>• <strong>ANR的监测机制</strong>，从Service，Broadcast，InputEvent三种不同的ANR监测机制的源码实现开始，分析了Android如何发现各类ANR。在启动服务、派发广播消息和输入事件时，植入超时检测，用于发现ANR<br>• <strong>ANR的报告机制</strong>，分析Android如何输出ANR日志。当ANR被发现后，两个很重要的日志输出是：CPU使用情况和进程的函数调用栈，这两类日志是我们解决ANR问题的利器</p><p>ANR的解决方法，通过一个案例，对ANR日志进行了深入解读，梳理了分析ANR问题的思路和途径<br>最后，致各位读者，从日志出发解决ANR问题，理解ANR机制背后的实现原理，碰到再难的ANR问题也无需惊慌。</p><p> <strong>Android中的各种log机制以及分析。</strong></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><a href="http://duanqz.github.io/2015-10-12-ANR-Analysis" target="_blank" rel="noopener">ANR机制以及问题分析</a></li><li><a href="https://blog.csdn.net/mathcompfrac/article/details/53862441" target="_blank" rel="noopener">ANR问题分析流程</a></li><li><a href="http://duanqz.github.io/2015-11-04-StrictMode-Analysis" target="_blank" rel="noopener">StrictMode 机制以及使用场景</a></li><li><a href="http://duanqz.github.io/2015-10-12-Watchdog-Analysis#section-2" target="_blank" rel="noopener">Watchdog机制以及问题分析</a></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;本文目标讲解Android中的ANR原理以及如何分析ANR问题&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;   ANR(Application Not Respondi
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="ANR" scheme="http://yoursite.com/categories/Android/ANR/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="ANR" scheme="http://yoursite.com/tags/ANR/"/>
    
  </entry>
  
  <entry>
    <title>Android8.0充电图标不显示</title>
    <link href="http://yoursite.com/Android8-0-charging-icon-cannot-show/"/>
    <id>http://yoursite.com/Android8-0-charging-icon-cannot-show/</id>
    <published>2018-04-25T09:06:20.000Z</published>
    <updated>2018-08-07T07:48:17.416Z</updated>
    
    <content type="html"><![CDATA[<p>1.充电图标不显示，问题分析步骤<br>a)找到充电图标的UI，frameworks/base/packages/SystemUI/res/layout/system_icons.xml<br>中找到<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;com.android.systemui.BatteryMeterView android:id=<span class="string">"@+id/battery"</span></span><br><span class="line">        android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">        android:layout_width=<span class="string">"wrap_content"</span></span><br><span class="line">        /&gt;</span><br></pre></td></tr></table></figure></p><p>确定充电图标UI文件，BatteryMeterView.java<br>frameworks/base/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java<br>中充电图标绘制在BatteryMeterDrawableBase.java,查看<br>frameworks/base/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java可以看到充电的闪电图标的绘制过程。<br>在BatteryMeterView.java中可以看到充电图标的显示与否是由下面决定的:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) &#123;</span><br><span class="line">        mDrawable.setBatteryLevel(level);</span><br><span class="line">        mDrawable.setCharging(pluggedIn);//pluggedIn为<span class="literal">true</span>，显示闪电图标，为<span class="literal">false</span>则不显示</span><br><span class="line">        mLevel = level;</span><br><span class="line">        updatePercentText();</span><br><span class="line">        setContentDescription(</span><br><span class="line">                getContext().getString(charging ? R.string.accessibility_battery_level_charging</span><br><span class="line">                        : R.string.accessibility_battery_level, level));</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>可以看到是由pluggedIn来决定是否显示，这里有个疑问，为何不用charging的值来判断，而要用pluggedIn的值来判断。原因在下面这个代码,<br>onBatteryLevelChanged这个函数，是在<br>frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java里面addCallback回调过去的。<br>mCharging的值是由 mCharged和 BATTERY_STATUS_CHARGING两个值一起判断的。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    public void onReceive(final Context context, Intent intent) &#123;</span><br><span class="line">        final String action = intent.getAction();</span><br><span class="line">        <span class="keyword">if</span> (action.equals(Intent.ACTION_BATTERY_CHANGED)) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mTestmode &amp;&amp; !intent.getBooleanExtra(<span class="string">"testmode"</span>, <span class="literal">false</span>)) <span class="built_in">return</span>;</span><br><span class="line">            mHasReceivedBattery = <span class="literal">true</span>;</span><br><span class="line">            mLevel = (int)(100f</span><br><span class="line">                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)</span><br><span class="line">                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));</span><br><span class="line">            mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;</span><br><span class="line"></span><br><span class="line">            final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,</span><br><span class="line">                    BatteryManager.BATTERY_STATUS_UNKNOWN);</span><br><span class="line">            mCharged = status == BatteryManager.BATTERY_STATUS_FULL;</span><br><span class="line">            mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;</span><br><span class="line"></span><br><span class="line">            fireBatteryLevelChanged();</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></p><p>为了查看手机中当前的pluggedIn和charging这两个值的状态，从<br>frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java文件中看到了一个Dump 的log打印，刚好打印了这几个值的状态，如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) &#123;</span><br><span class="line">        pw.println(<span class="string">"BatteryController state:"</span>);</span><br><span class="line">        pw.print(<span class="string">"  mLevel="</span>); pw.println(mLevel);</span><br><span class="line">        pw.print(<span class="string">"  mPluggedIn="</span>); pw.println(mPluggedIn);</span><br><span class="line">        pw.print(<span class="string">"  mCharging="</span>); pw.println(mCharging);</span><br><span class="line">        pw.print(<span class="string">"  mCharged="</span>); pw.println(mCharged);</span><br><span class="line">        pw.print(<span class="string">"  mPowerSave="</span>); pw.println(mPowerSave);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><pre><code>所以，不需要另外打任何的log，直接连接手机，使用adb shell bugreport &gt; bugreport.txt抓出所有的dump log即可。BatteryController state:  mLevel=86  mPluggedIn=false  mCharging=true  mCharged=false  mPowerSave=false01-01 10:23:19.490  1000  1098  1110 D BatteryService: Sending ACTION_BATTERY_CHANGED.  level:71, scale:100, status:3, health:2, present:true, voltage: 3962, temperature: 375, technology: Li-ion, AC powered:false, USB powered:false, Wireless powered:false, icon:17303429, invalid charger:0, maxChargingCurrent:0, maxChargingVoltage:0, chargeCounter:1714883-------------------------------------------------------------------------------DUMP OF SERVICE batteryproperties:ac: 0 usb: 0 wireless: 0 current_max: 0 voltage_max: 0status: 2 health: 2 present: 1level: 86 voltage: 4234 temp: 297current now: -77209charge counter: 2089873current now: -376Full charge: 2444000--------- 0.003s was the duration of dumpsys batteryproperties, ending at: 2018-06-15 16:17:57这里有个问题，adb shell bugreport的log在User版本和Userdebug的版本里面抓出来的结果不一样，具体原因，可能是日志的级别限制的，需要查证一下。</code></pre><p>2.mPluggedIn的值是在收到ACTION_BATTERY_CHANGED广播里面带的参数得到的，那么这个广播是哪里发出来的呢？<br>通过使用<a href="http://androidxref.com" target="_blank" rel="noopener">http://androidxref.com</a> 进行全局搜索，找到是在<br>/frameworks/base/services/core/java/com/android/server/BatteryService.java的sendIntentLocked()函数里面发出来的。代码如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">private void <span class="function"><span class="title">sendIntentLocked</span></span>() &#123;</span><br><span class="line">        //  Pack up the values and broadcast them to everyone</span><br><span class="line">        final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);</span><br><span class="line">        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY</span><br><span class="line">                | Intent.FLAG_RECEIVER_REPLACE_PENDING);</span><br><span class="line"></span><br><span class="line">        int icon = getIconLocked(mBatteryProps.batteryLevel);</span><br><span class="line"></span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);</span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);    </span><br><span class="line">        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);  </span><br><span class="line"></span><br><span class="line">        EXTRA_PLUGGED的值是由mPlugType得到的，而mPlugType的值在BatteryService.java中得到方式如下：</span><br><span class="line">    private void processValuesLocked(boolean force) &#123;</span><br><span class="line">        boolean logOutlier = <span class="literal">false</span>;</span><br><span class="line">        long dischargeDuration = 0;</span><br><span class="line"></span><br><span class="line">        mBatteryLevelCritical = (mBatteryProps.batteryLevel &lt;= mCriticalBatteryLevel);</span><br><span class="line">        <span class="keyword">if</span> (mBatteryProps.chargerAcOnline) &#123;</span><br><span class="line">            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mBatteryProps.chargerUsbOnline) &#123;</span><br><span class="line">            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mBatteryProps.chargerWirelessOnline) &#123;</span><br><span class="line">            mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mPlugType = BATTERY_PLUGGED_NONE;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></p><pre><code>从中可以看出是根据mBatteryProps.chargerAcOnline或者mBatteryProps.chargerUsbOnline或者mBatteryProps.chargerWirelessOnline的值来判断，在frameworks/base/core/java/android/os/BatteryProperties.java中，可以看到这三个值是通过序列化的方式得到：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">private BatteryProperties(Parcel p) &#123;</span><br><span class="line">    chargerAcOnline = p.readInt() == 1 ? <span class="literal">true</span> : <span class="literal">false</span>;</span><br><span class="line">    chargerUsbOnline = p.readInt() == 1 ? <span class="literal">true</span> : <span class="literal">false</span>;</span><br><span class="line">    chargerWirelessOnline = p.readInt() == 1 ? <span class="literal">true</span> : <span class="literal">false</span>;</span><br><span class="line">    maxChargingCurrent = p.readInt();</span><br><span class="line">    maxChargingVoltage = p.readInt();</span><br><span class="line">    batteryStatus = p.readInt();</span><br><span class="line">    batteryHealth = p.readInt();</span><br><span class="line">    batteryPresent = p.readInt() == 1 ? <span class="literal">true</span> : <span class="literal">false</span>;</span><br><span class="line">    batteryLevel = p.readInt();</span><br><span class="line">    batteryVoltage = p.readInt();</span><br><span class="line">    batteryTemperature = p.readInt();</span><br><span class="line">    batteryFullCharge = p.readInt();</span><br><span class="line">    batteryChargeCounter = p.readInt();</span><br><span class="line">    batteryTechnology = p.readString();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public void writeToParcel(Parcel p, int flags) &#123;</span><br><span class="line">    p.writeInt(chargerAcOnline ? 1 : 0);</span><br><span class="line">    p.writeInt(chargerUsbOnline ? 1 : 0);</span><br><span class="line">    p.writeInt(chargerWirelessOnline ? 1 : 0);</span><br><span class="line">    p.writeInt(maxChargingCurrent);</span><br><span class="line">    p.writeInt(maxChargingVoltage);</span><br><span class="line">    p.writeInt(batteryStatus);</span><br><span class="line">    p.writeInt(batteryHealth);</span><br><span class="line">    p.writeInt(batteryPresent ? 1 : 0);</span><br><span class="line">    p.writeInt(batteryLevel);</span><br><span class="line">    p.writeInt(batteryVoltage);</span><br><span class="line">    p.writeInt(batteryTemperature);</span><br><span class="line">    p.writeInt(batteryFullCharge);</span><br><span class="line">    p.writeInt(batteryChargeCounter);</span><br><span class="line">    p.writeString(batteryTechnology);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public static final Parcelable.Creator&lt;BatteryProperties&gt; CREATOR</span><br><span class="line">    = new Parcelable.Creator&lt;BatteryProperties&gt;() &#123;</span><br><span class="line">    public BatteryProperties createFromParcel(Parcel p) &#123;</span><br><span class="line">        <span class="built_in">return</span> new BatteryProperties(p);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public BatteryProperties[] newArray(int size) &#123;</span><br><span class="line">        <span class="built_in">return</span> new BatteryProperties[size];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><pre><code>再来看看mBatteryProps的赋值：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">private void update(BatteryProperties props) &#123;</span><br><span class="line">    synchronized (mLock) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!mUpdatesStopped) &#123;</span><br><span class="line">            mBatteryProps = props;</span><br><span class="line">            // Process the new values.</span><br><span class="line">            processValuesLocked(<span class="literal">false</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mLastBatteryProps.set(props);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>接着看update()函数的调用，如下：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">private final class BatteryListener extends IBatteryPropertiesListener.Stub &#123;</span><br><span class="line">    @Override public void batteryPropertiesChanged(BatteryProperties props) &#123;</span><br><span class="line">        final long identity = Binder.clearCallingIdentity();</span><br><span class="line">        try &#123;</span><br><span class="line">            BatteryService.this.update(props);</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            Binder.restoreCallingIdentity(identity);</span><br><span class="line">        &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>可以看到BatteryProperties是在由batteryPropertiesChanged赋值，是通过AIDL实现对C++侧的监听，这个AIDL实现了JAVA侧与C++侧的通讯。这一块关于电池的共有三个AIDL文件,作为客户端和服务端的媒介，具体代码，以及作用分析如下：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/core/java/android/os/BatteryProperties.aidl</span><br><span class="line">package android.os;</span><br><span class="line">    parcelable BatteryProperties;</span><br></pre></td></tr></table></figure><pre><code>BatteryProperties.aidl主要用户AIDL跨进程通讯时的数据序列化和反序列化frameworks/base/core/java/android/os/BatteryProperties.java</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/core/java/android/os/IBatteryPropertiesListener.aidl</span><br><span class="line">package android.os;</span><br><span class="line">import android.os.BatteryProperties;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line">* &#123;@hide&#125;</span><br><span class="line">*/</span><br><span class="line"></span><br><span class="line">oneway interface IBatteryPropertiesListener &#123;</span><br><span class="line">    void batteryPropertiesChanged(<span class="keyword">in</span> BatteryProperties props);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>IBatteryPropertiesListener.aidl主要用于监听</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/core/java/android/os/IBatteryPropertiesRegistrar.aidl</span><br><span class="line">package android.os;</span><br><span class="line">import android.os.IBatteryPropertiesListener;</span><br><span class="line">import android.os.BatteryProperty;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * &#123;@hide&#125;</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">interface IBatteryPropertiesRegistrar &#123;</span><br><span class="line">    void registerListener(IBatteryPropertiesListener listener);</span><br><span class="line">    void unregisterListener(IBatteryPropertiesListener listener);</span><br><span class="line">    int getProperty(<span class="keyword">in</span> int id, out BatteryProperty prop);</span><br><span class="line">    oneway void scheduleUpdate();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>IBatteryPropertiesRegistrar.aidl主要用于注册服务端在BatteryService.java</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public void <span class="function"><span class="title">onStart</span></span>() &#123;</span><br><span class="line">    IBinder b = ServiceManager.getService(<span class="string">"batteryproperties"</span>);</span><br><span class="line">    final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =</span><br><span class="line">            IBatteryPropertiesRegistrar.Stub.asInterface(b);</span><br><span class="line">    try &#123;</span><br><span class="line">        batteryPropertiesRegistrar.registerListener(new BatteryListener());</span><br><span class="line">    &#125; catch (RemoteException e) &#123;</span><br><span class="line">        // Should never happen.</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mBinderService = new BinderService();</span><br><span class="line">    publishBinderService(<span class="string">"battery"</span>, mBinderService);</span><br><span class="line">    publishLocalService(BatteryManagerInternal.class, new LocalService());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>对应的native层的代码在frameworks/native/services/batteryservice/编译出libbatteryservice.so库Java侧与C++侧通过AIDL通讯，两边都要写AIDL文件，且两边都要有Parcel序列化。通过查找batteryPropertiesChanged函数system/core/healthd/BatteryPropertiesRegistrar.cpp</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include "BatteryPropertiesRegistrar.h"</span></span><br><span class="line"><span class="comment">#include &lt;batteryservice/BatteryService.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;batteryservice/IBatteryPropertiesListener.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;batteryservice/IBatteryPropertiesRegistrar.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;binder/IPCThreadState.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;binder/IServiceManager.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;binder/PermissionCache.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;private/android_filesystem_config.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;utils/Errors.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;utils/Mutex.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;utils/String16.h&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#include &lt;healthd/healthd.h&gt;</span></span><br><span class="line"></span><br><span class="line">namespace android &#123;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::publish(</span><br><span class="line">    const sp&lt;BatteryPropertiesRegistrar&gt;&amp; service) &#123;</span><br><span class="line">    defaultServiceManager()-&gt;addService(String16(<span class="string">"batteryproperties"</span>), service);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties&amp; props) &#123;</span><br><span class="line">    Vector&lt;sp&lt;IBatteryPropertiesListener&gt; &gt; listenersCopy;</span><br><span class="line"></span><br><span class="line">    // Binder currently may service an incoming oneway transaction whenever an</span><br><span class="line">    // outbound oneway call is made (<span class="keyword">if</span> there is already a pending incoming</span><br><span class="line">    // oneway call waiting).  This is considered a bug and may change <span class="keyword">in</span> the</span><br><span class="line">    // future.  For now, avoid recursive mutex lock <span class="keyword">while</span> making outbound</span><br><span class="line">    // calls by making a <span class="built_in">local</span> copy of the current list of listeners.</span><br><span class="line">    &#123;</span><br><span class="line">        Mutex::Autolock _l(mRegistrationLock);</span><br><span class="line">        listenersCopy = mListeners;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (size_t i = 0; i &lt; listenersCopy.size(); i++) &#123;</span><br><span class="line">        listenersCopy[i]-&gt;batteryPropertiesChanged(props);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::registerListener(const sp&lt;IBatteryPropertiesListener&gt;&amp; listener) &#123;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (listener == NULL)</span><br><span class="line">            <span class="built_in">return</span>;</span><br><span class="line">        Mutex::Autolock _l(mRegistrationLock);</span><br><span class="line">        // check whether this is a duplicate</span><br><span class="line">        <span class="keyword">for</span> (size_t i = 0; i &lt; mListeners.size(); i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) &#123;</span><br><span class="line">                <span class="built_in">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        mListeners.add(listener);</span><br><span class="line">        IInterface::asBinder(listener)-&gt;linkToDeath(this);</span><br><span class="line">    &#125;</span><br><span class="line">    healthd_battery_update();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::unregisterListener(const sp&lt;IBatteryPropertiesListener&gt;&amp; listener) &#123;</span><br><span class="line">    <span class="keyword">if</span> (listener == NULL)</span><br><span class="line">        <span class="built_in">return</span>;</span><br><span class="line">    Mutex::Autolock _l(mRegistrationLock);</span><br><span class="line">    <span class="keyword">for</span> (size_t i = 0; i &lt; mListeners.size(); i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) &#123;</span><br><span class="line">            IInterface::asBinder(mListeners[i])-&gt;unlinkToDeath(this);</span><br><span class="line">            mListeners.removeAt(i);</span><br><span class="line">            <span class="built_in">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) &#123;</span><br><span class="line">    <span class="built_in">return</span> healthd_get_property(id, val);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::<span class="function"><span class="title">scheduleUpdate</span></span>() &#123;</span><br><span class="line">    healthd_battery_update();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">status_t BatteryPropertiesRegistrar::dump(int fd, const Vector&lt;String16&gt;&amp; /*args*/) &#123;</span><br><span class="line">    IPCThreadState* self = IPCThreadState::self();</span><br><span class="line">    const int pid = self-&gt;getCallingPid();</span><br><span class="line">    const int uid = self-&gt;getCallingUid();</span><br><span class="line">    <span class="keyword">if</span> ((uid != AID_SHELL) &amp;&amp;</span><br><span class="line">        !PermissionCache::checkPermission(</span><br><span class="line">                String16(<span class="string">"android.permission.DUMP"</span>), pid, uid))</span><br><span class="line">        <span class="built_in">return</span> PERMISSION_DENIED;</span><br><span class="line"></span><br><span class="line">    healthd_dump_battery_state(fd);</span><br><span class="line">    <span class="built_in">return</span> OK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void BatteryPropertiesRegistrar::binderDied(const wp&lt;IBinder&gt;&amp; who) &#123;</span><br><span class="line">    Mutex::Autolock _l(mRegistrationLock);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (size_t i = 0; i &lt; mListeners.size(); i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (IInterface::asBinder(mListeners[i]) == who) &#123;</span><br><span class="line">            mListeners.removeAt(i);</span><br><span class="line">            <span class="built_in">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;  // namespace android</span><br></pre></td></tr></table></figure><pre><code>再查找notifyListeners函数，在 system/core/healthd/healthd_mode_android.cpp</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">void healthd_mode_android_battery_update(</span><br><span class="line">    struct android::BatteryProperties *props) &#123;</span><br><span class="line">    <span class="keyword">if</span> (gBatteryPropertiesRegistrar != NULL)</span><br><span class="line">        gBatteryPropertiesRegistrar-&gt;notifyListeners(*props);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><pre><code>Healthd是一种中介模型，向下监听来自kernel层的电池事件，向上将电池数据信息传递给Framework层的BatteryService.主要代码在 system/core/healthd 中，对Healthd模块的代码分析如下：参考：Healthd模块编译出来的文件有system/bin/healthd通过在system/core/healthd里面查找chargerUsbOnline，在system/core/healthd/BatteryMonitor.cpp中update函数可以看到赋值：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br></pre></td><td class="code"><pre><span class="line">bool BatteryMonitor::update(void) &#123;</span><br><span class="line">    bool logthis;</span><br><span class="line"></span><br><span class="line">    initBatteryProperties(&amp;props);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryPresentPath.isEmpty())</span><br><span class="line">        props.batteryPresent = getBooleanField(mHealthdConfig-&gt;batteryPresentPath);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        props.batteryPresent = mBatteryDevicePresent;</span><br><span class="line"></span><br><span class="line">    props.batteryLevel = mBatteryFixedCapacity ?</span><br><span class="line">        mBatteryFixedCapacity :</span><br><span class="line">        getIntField(mHealthdConfig-&gt;batteryCapacityPath);</span><br><span class="line">    props.batteryVoltage = getIntField(mHealthdConfig-&gt;batteryVoltagePath) / 1000;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryCurrentNowPath.isEmpty())</span><br><span class="line">        props.batteryCurrent = getIntField(mHealthdConfig-&gt;batteryCurrentNowPath) / 1000;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryFullChargePath.isEmpty())</span><br><span class="line">        props.batteryFullCharge = getIntField(mHealthdConfig-&gt;batteryFullChargePath);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryCycleCountPath.isEmpty())</span><br><span class="line">        props.batteryCycleCount = getIntField(mHealthdConfig-&gt;batteryCycleCountPath);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryChargeCounterPath.isEmpty())</span><br><span class="line">        props.batteryChargeCounter = getIntField(mHealthdConfig-&gt;batteryChargeCounterPath);</span><br><span class="line"></span><br><span class="line">    props.batteryTemperature = mBatteryFixedTemperature ?</span><br><span class="line">        mBatteryFixedTemperature :</span><br><span class="line">        getIntField(mHealthdConfig-&gt;batteryTemperaturePath);</span><br><span class="line"></span><br><span class="line">    // For devices <span class="built_in">which</span> <span class="keyword">do</span> not have battery and are always plugged</span><br><span class="line">    // into power souce.</span><br><span class="line">    <span class="keyword">if</span> (mAlwaysPluggedDevice) &#123;</span><br><span class="line">        props.chargerAcOnline = <span class="literal">true</span>;</span><br><span class="line">        props.batteryPresent = <span class="literal">true</span>;</span><br><span class="line">        props.batteryStatus = BATTERY_STATUS_CHARGING;</span><br><span class="line">        props.batteryHealth = BATTERY_HEALTH_GOOD;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    std::string buf;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (readFromFile(mHealthdConfig-&gt;batteryStatusPath, &amp;buf) &gt; 0)</span><br><span class="line">        props.batteryStatus = getBatteryStatus(buf.c_str());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (readFromFile(mHealthdConfig-&gt;batteryHealthPath, &amp;buf) &gt; 0)</span><br><span class="line">        props.batteryHealth = getBatteryHealth(buf.c_str());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (readFromFile(mHealthdConfig-&gt;batteryTechnologyPath, &amp;buf) &gt; 0)</span><br><span class="line">        props.batteryTechnology = String8(buf.c_str());</span><br><span class="line"></span><br><span class="line">    unsigned int i;</span><br><span class="line">    double MaxPower = 0;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = 0; i &lt; mChargerNames.size(); i++) &#123;</span><br><span class="line">        String8 path;</span><br><span class="line">        path.appendFormat(<span class="string">"%s/%s/online"</span>, POWER_SUPPLY_SYSFS_PATH,</span><br><span class="line">                          mChargerNames[i].string());</span><br><span class="line">        <span class="keyword">if</span> (getIntField(path)) &#123;</span><br><span class="line">            path.clear();</span><br><span class="line">            path.appendFormat(<span class="string">"%s/%s/type"</span>, POWER_SUPPLY_SYSFS_PATH,</span><br><span class="line">                              mChargerNames[i].string());</span><br><span class="line">            switch(readPowerSupplyType(path)) &#123;</span><br><span class="line">            <span class="keyword">case</span> ANDROID_POWER_SUPPLY_TYPE_AC:</span><br><span class="line">                props.chargerAcOnline = <span class="literal">true</span>;</span><br><span class="line">                <span class="built_in">break</span>;</span><br><span class="line">            <span class="keyword">case</span> ANDROID_POWER_SUPPLY_TYPE_USB:</span><br><span class="line">                props.chargerUsbOnline = <span class="literal">true</span>;</span><br><span class="line">                <span class="built_in">break</span>;</span><br><span class="line">            <span class="keyword">case</span> ANDROID_POWER_SUPPLY_TYPE_WIRELESS:</span><br><span class="line">                props.chargerWirelessOnline = <span class="literal">true</span>;</span><br><span class="line">                <span class="built_in">break</span>;</span><br><span class="line">            default:</span><br><span class="line">                KLOG_WARNING(LOG_TAG, <span class="string">"%s: Unknown power supply type\n"</span>,</span><br><span class="line">                             mChargerNames[i].string());</span><br><span class="line">            &#125;</span><br><span class="line">            path.clear();</span><br><span class="line">            path.appendFormat(<span class="string">"%s/%s/current_max"</span>, POWER_SUPPLY_SYSFS_PATH,</span><br><span class="line">                              mChargerNames[i].string());</span><br><span class="line">            int ChargingCurrent =</span><br><span class="line">                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;</span><br><span class="line"></span><br><span class="line">            path.clear();</span><br><span class="line">            path.appendFormat(<span class="string">"%s/%s/voltage_max"</span>, POWER_SUPPLY_SYSFS_PATH,</span><br><span class="line">                              mChargerNames[i].string());</span><br><span class="line"></span><br><span class="line">            int ChargingVoltage =</span><br><span class="line">                (access(path.string(), R_OK) == 0) ? getIntField(path) :</span><br><span class="line">                DEFAULT_VBUS_VOLTAGE;</span><br><span class="line"></span><br><span class="line">            double power = ((double)ChargingCurrent / MILLION) *</span><br><span class="line">                           ((double)ChargingVoltage / MILLION);</span><br><span class="line">            <span class="keyword">if</span> (MaxPower &lt; power) &#123;</span><br><span class="line">                props.maxChargingCurrent = ChargingCurrent;</span><br><span class="line">                props.maxChargingVoltage = ChargingVoltage;</span><br><span class="line">                MaxPower = power;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    logthis = !healthd_board_battery_update(&amp;props);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        char dmesgline[256];</span><br><span class="line">        size_t len;</span><br><span class="line">        <span class="keyword">if</span> (props.batteryPresent) &#123;</span><br><span class="line">            snprintf(dmesgline, sizeof(dmesgline),</span><br><span class="line">                 <span class="string">"battery l=%d v=%d t=%s%d.%d h=%d st=%d"</span>,</span><br><span class="line">                 props.batteryLevel, props.batteryVoltage,</span><br><span class="line">                 props.batteryTemperature &lt; 0 ? <span class="string">"-"</span> : <span class="string">""</span>,</span><br><span class="line">                 abs(props.batteryTemperature / 10),</span><br><span class="line">                 abs(props.batteryTemperature % 10), props.batteryHealth,</span><br><span class="line">                 props.batteryStatus);</span><br><span class="line"></span><br><span class="line">            len = strlen(dmesgline);</span><br><span class="line">            <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryCurrentNowPath.isEmpty()) &#123;</span><br><span class="line">                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,</span><br><span class="line">                                <span class="string">" c=%d"</span>, props.batteryCurrent);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryFullChargePath.isEmpty()) &#123;</span><br><span class="line">                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,</span><br><span class="line">                                <span class="string">" fc=%d"</span>, props.batteryFullCharge);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!mHealthdConfig-&gt;batteryCycleCountPath.isEmpty()) &#123;</span><br><span class="line">                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,</span><br><span class="line">                                <span class="string">" cc=%d"</span>, props.batteryCycleCount);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            len = snprintf(dmesgline, sizeof(dmesgline),</span><br><span class="line">                 <span class="string">"battery none"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        snprintf(dmesgline + len, sizeof(dmesgline) - len, <span class="string">" chg=%s%s%s"</span>,</span><br><span class="line">                 props.chargerAcOnline ? <span class="string">"a"</span> : <span class="string">""</span>,</span><br><span class="line">                 props.chargerUsbOnline ? <span class="string">"u"</span> : <span class="string">""</span>,</span><br><span class="line">                 props.chargerWirelessOnline ? <span class="string">"w"</span> : <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line">        KLOG_WARNING(LOG_TAG, <span class="string">"%s\n"</span>, dmesgline);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    healthd_mode_ops-&gt;battery_update(&amp;props);</span><br><span class="line">    <span class="built_in">return</span> props.chargerAcOnline | props.chargerUsbOnline |</span><br><span class="line">            props.chargerWirelessOnline;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>   从上面代码可以看到chargerUsbOnline的值是通过读取 sys/class/power_supply文件夹下的USB文件夹下的type里面的值来判断。</p><p>   adb shell进入手机后，cd sys/class/power_supply/USB,cat type发现是USB。<br>   这下懵逼了，这个值是对的呀，为何log里面读取出来的值不对呢？而且从之前抓的dump log来看如下：的确没有正确的读取到</p><pre><code>-------------------------------------------------------------------------------DUMP OF SERVICE batteryproperties:ac: 0 usb: 0 wireless: 0 current_max: 0 voltage_max: 0status: 2 health: 2 present: 1level: 86 voltage: 4234 temp: 297current now: -77209charge counter: 2089873current now: -376Full charge: 2444000--------- 0.003s was the duration of dumpsys batteryproperties, ending at: 2018-06-15 16:17:57现在怀疑是SeAndroid的AVC权限导致的。</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;1.充电图标不显示，问题分析步骤&lt;br&gt;a)找到充电图标的UI，frameworks/base/packages/SystemUI/res/layout/system_icons.xml&lt;br&gt;中找到&lt;br&gt;&lt;figure class=&quot;highlight bash&quot;&gt;&lt;t
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android PMS" scheme="http://yoursite.com/tags/Android-PMS/"/>
    
  </entry>
  
  <entry>
    <title>Watchdog机制以及问题分析</title>
    <link href="http://yoursite.com/Android-Watchdog-Analysis/"/>
    <id>http://yoursite.com/Android-Watchdog-Analysis/</id>
    <published>2018-04-16T13:23:33.000Z</published>
    <updated>2018-08-07T06:55:39.715Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h1><p>Watchdog的中文的“看门狗”，有保护的意思。最早引入Watchdog是在单片机系统中，由于单片机的工作环境容易受到外界磁场的干扰，导致程序“跑飞”，造成整个系统无法正常工作，因此，引入了一个“看门狗”，对单片机的运行状态进行实时监测，针对运行故障做一些保护处理，譬如让系统重启。这种Watchdog属于硬件层面，必须有硬件电路的支持。</p><p>Linux也引入了Watchdog，在Linux内核下，当Watchdog启动后，便设定了一个定时器，如果在超时时间内没有对/dev/Watchdog进行写操作，则会导致系统重启。通过定时器实现的Watchdog属于软件层面。</p><p>Android设计了一个软件层面Watchdog，用于保护一些重要的系统服务，当出现故障时，通常会让Android系统重启。由于这种机制的存在，就经常会出现一些system_server进程被Watchdog杀掉而发生手机重启的问题。</p><p>本文期望回答以下问题：</p><ul><li>1)Watchdog是怎么工作的？这涉及到Watchdog的工作机制。</li><li>2)遇到Watchdog的问题该怎么办？这涉及到分析Watchdog问题的惯用方法。</li></ul><p><strong>本文主要分析的是Framework中的Watchdog，底层(Kernel)侧也有Watchdog机制。但本文不分析。</strong></p><h1 id="Watchdog机制"><a href="#Watchdog机制" class="headerlink" title="Watchdog机制"></a>Watchdog机制</h1><p>本文以<a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/Watchdog.java" target="_blank" rel="noopener">frameworks/base/services/core/java/com/android/server/Watchdog.java</a>为蓝本，分析Watchdog的实现逻辑。为了描述方便，ActivityManagerService， PackageManagerService， WindowManagerService会分别简称为AMS, PKMS, WMS。</p><h2 id="Watchdog的初始化"><a href="#Watchdog的初始化" class="headerlink" title="Watchdog的初始化"></a>Watchdog的初始化</h2><p>Android的Watchdog是一个单例线程，在System Server时就会初始化Watchdog。Watchdog在初始化时，会构建很多HandlerChecker，大致可以分为两类：</p><ul><li>Monitor Checker，用于检查是Monitor对象可能发生的死锁, AMS, PKMS, WMS等核心的系统服务都是Monitor对象。</li><li>Looper Checker，用于检查线程的消息队列是否长时间处于工作状态。Watchdog自身的消息队列，Ui, Io, Display这些全局的消息队列都是被检查的对象。此外，一些重要的线程的消息队列，也会加入到Looper Checker中，譬如AMS, PKMS，这些是在对应的对象初始化时加入的。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">Watchdog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ....</span><br><span class="line">    mMonitorChecker = <span class="keyword">new</span> HandlerChecker(FgThread.getHandler(),</span><br><span class="line">                <span class="string">"foreground thread"</span>, DEFAULT_TIMEOUT);</span><br><span class="line">    mHandlerCheckers.add(mMonitorChecker);</span><br><span class="line">    mHandlerCheckers.add(<span class="keyword">new</span> HandlerChecker(<span class="keyword">new</span> Handler(Looper.getMainLooper()),</span><br><span class="line">                <span class="string">"main thread"</span>, DEFAULT_TIMEOUT));</span><br><span class="line">    mHandlerCheckers.add(<span class="keyword">new</span> HandlerChecker(UiThread.getHandler(),</span><br><span class="line">                <span class="string">"ui thread"</span>, DEFAULT_TIMEOUT));</span><br><span class="line">    mHandlerCheckers.add(<span class="keyword">new</span> HandlerChecker(IoThread.getHandler(),</span><br><span class="line">                <span class="string">"i/o thread"</span>, DEFAULT_TIMEOUT));</span><br><span class="line">    mHandlerCheckers.add(<span class="keyword">new</span> HandlerChecker(DisplayThread.getHandler(),</span><br><span class="line">                <span class="string">"display thread"</span>, DEFAULT_TIMEOUT));</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>两类HandlerChecker的侧重点不同，Monitor Checker预警我们不能长时间持有核心系统服务的对象锁，否则会阻塞很多函数的运行; Looper Checker预警我们不能长时间的霸占消息队列，否则其他消息将得不到处理。这两类都会导致系统卡住(System Not Responding)。</p><h2 id="添加Watchdog监测对象"><a href="#添加Watchdog监测对象" class="headerlink" title="添加Watchdog监测对象"></a>添加Watchdog监测对象</h2><p>Watchdog初始化以后，就可以作为system_server进程中的一个单独的线程运行了。但这个时候，还不能触发Watchdog的运行，因为AMS, PKMS等系统服务还没有加入到Watchdog的监测集。 所谓监测集，就是需要Watchdog关注的对象，Android中有成千上万的消息队列在同时运行，然而，Watchdog毕竟是系统层面的东西，它只会关注一些核心的系统服务。</p><p>Watchdog提供两个方法，分别用于添加Monitor Checker对象和Looper Checker对象:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addMonitor</span><span class="params">(Monitor monitor)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 将monitor对象添加到Monitor Checker中，</span></span><br><span class="line">    <span class="comment">// 在Watchdog初始化时，可以看到Monitor Checker本身也是一个HandlerChecker对象</span></span><br><span class="line">    mMonitors.add(monitor);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addThread</span><span class="params">(Handler thread, <span class="keyword">long</span> timeoutMillis)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isAlive()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Threads can't be added once the Watchdog is running"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">final</span> String name = thread.getLooper().getThread().getName();</span><br><span class="line">        <span class="comment">// 为Handler构建一个HandlerChecker对象，其实就是**Looper Checker**</span></span><br><span class="line">        mHandlerCheckers.add(<span class="keyword">new</span> HandlerChecker(thread, name, timeoutMillis));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>被Watchdog监测的对象，都需要将自己添加到Watchdog的监测集中。以下是AMS的类定义和构造器的代码片段：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ActivityManagerService</span> <span class="keyword">extends</span> <span class="title">ActivityManagerNative</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">Watchdog</span>.<span class="title">Monitor</span>, <span class="title">BatteryStatsImpl</span>.<span class="title">BatteryCallback</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ActivityManagerService</span><span class="params">(Context systemContext)</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        Watchdog.getInstance().addMonitor(<span class="keyword">this</span>);</span><br><span class="line">        Watchdog.getInstance().addThread(mHandler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">monitor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>AMS实现了Watchdog.Monitor接口，这个接口只有一个方法，就是monitor()，它的作用后文会再解释。这里可以看到在AMS的构造器中，将自己添加到Monitor Checker对象中，然后将自己的handler添加到Looper Checker对象中。 其他重要的系统服务添加到Watchdog的代码逻辑都与AMS差不多。</p><p>整个Android系统中，被monitor的对象并不多，十个手指头就能数出来Watchdog.Monitor的实现类的个数。</p><h2 id="Watchdog的监测机制"><a href="#Watchdog的监测机制" class="headerlink" title="Watchdog的监测机制"></a>Watchdog的监测机制</h2><p>Watchdog本身是一个线程，它的run()方法实现如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> waitedHalf = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">// 1. 调度所有的HandlerChecker</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i=<span class="number">0</span>; i&lt;mHandlerCheckers.size(); i++) &#123;</span><br><span class="line">                HandlerChecker hc = mHandlerCheckers.get(i);</span><br><span class="line">                hc.scheduleCheckLocked();</span><br><span class="line">            &#125;</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">// 2. 开始定期检查</span></span><br><span class="line">            <span class="keyword">long</span> start = SystemClock.uptimeMillis();</span><br><span class="line">            <span class="keyword">while</span> (timeout &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                ...</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    wait(timeout);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    Log.wtf(TAG, e);</span><br><span class="line">                &#125;</span><br><span class="line">                ...</span><br><span class="line">                timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 3. 检查HandlerChecker的完成状态</span></span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">int</span> waitState = evaluateCheckerCompletionLocked();</span><br><span class="line">            <span class="keyword">if</span> (waitState == COMPLETED) &#123;</span><br><span class="line">                ...</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (waitState == WAITING) &#123;</span><br><span class="line">                ...</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (waitState == WAITED_HALF) &#123;</span><br><span class="line">                ...</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 4. 存在超时的HandlerChecker</span></span><br><span class="line">            blockedCheckers = getBlockedCheckersLocked();</span><br><span class="line">            subject = describeCheckersLocked(blockedCheckers);</span><br><span class="line">            allowRestart = mAllowRestart;</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// 5. 保存日志，判断是否需要杀掉系统进程</span></span><br><span class="line">        Slog.w(TAG, <span class="string">"*** GOODBYE!"</span>);</span><br><span class="line">        Process.killProcess(Process.myPid());</span><br><span class="line">        System.exit(<span class="number">10</span>);</span><br><span class="line">    &#125; <span class="comment">// end of while (true)</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>以上代码片段主要的运行逻辑如下：</p><ol><li>Watchdog运行后，便开始无限循环，依次调用每一个HandlerChecker的scheduleCheckLocked()方法</li><li>调度完HandlerChecker之后，便开始定期检查是否超时，每一次检查的间隔时间由CHECK_INTERVAL常量设定，为30秒</li><li>每一次检查都会调用evaluateCheckerCompletionLocked()方法来评估一下HandlerChecker的完成状态：<ul><li>COMPLETED表示已经完成</li><li>WAITING和WAITED_HALF表示还在等待，但未超时</li><li>OVERDUE表示已经超时。默认情况下，timeout是1分钟，但监测对象可以通过传参自行设定，譬如PKMS的Handler Checker的超时是10分钟</li></ul></li><li>如果超时时间到了，还有HandlerChecker处于未完成的状态(OVERDUE)，则通过getBlockedCheckersLocked()方法，获取阻塞的HandlerChecker，生成一些描述信息</li><li>保存日志，包括一些运行时的堆栈信息，这些日志是我们解决Watchdog问题的重要依据。如果判断需要杀掉system_server进程，则给当前进程(system_server)发送signal 9</li></ol><p>只要Watchdog没有发现超时的任务，HandlerChecker就会被不停的调度，那HandlerChecker具体做一些什么检查呢？ 直接上代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">HandlerChecker</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scheduleCheckLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Looper Checker中是不包含monitor对象的，判断消息队列是否处于空闲</span></span><br><span class="line">        <span class="keyword">if</span> (mMonitors.size() == <span class="number">0</span> &amp;&amp; mHandler.getLooper().isIdling()) &#123;</span><br><span class="line">            mCompleted = <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// 将Monitor Checker的对象置于消息队列之前，优先运行</span></span><br><span class="line">        mHandler.postAtFrontOfQueue(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 依次调用Monitor对象的monitor()方法</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span> ; i &lt; size ; i++) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Watchdog.<span class="keyword">this</span>) &#123;</span><br><span class="line">                mCurrentMonitor = mMonitors.get(i);</span><br><span class="line">            &#125;</span><br><span class="line">            mCurrentMonitor.monitor();</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li>对于Looper Checker而言，会判断线程的消息队列是否处于空闲状态。 如果被监测的消息队列一直闲不下来，则说明可能已经阻塞等待了很长时间</li><li>对于Monitor Checker而言，会调用实现类的monitor方法，譬如上文中提到的AMS.monitor()方法， 方法实现一般很简单，就是获取当前类的对象锁，如果当前对象锁已经被持有，则monitor()会一直处于wait状态，直到超时，这种情况下，很可能是线程发生了死锁</li></ul><p>至此，我们已经分析了Watchdog的工作机制，回答了我们提出的第一个问题：<br>Watchdog定时检查一些重要的系统服务，举报长时间阻塞的事件，甚至杀掉system_server进程，让Android系统重启。</p><h1 id="问题分析方法"><a href="#问题分析方法" class="headerlink" title="问题分析方法"></a>问题分析方法</h1><h2 id="日志获取"><a href="#日志获取" class="headerlink" title="日志获取"></a>日志获取</h2><p>Andriod的日志门类繁多，而且，为了调试的需要，设备厂商和应用开发者都会在AOSP的基础上增加很多日志。 面对如此庞大复杂的日志系统，通常只有对应领域的专家才能看懂其透露的细节信息，就像去医院就诊，医生一看检查报告就知道患者身体出了什么问题，而外行对这些诊断信息往往是束手无策的。</p><p>解决Watchdog相关的问题，对日志的要求比较高，有些问题与当时的系统环境相关，仅仅凭借单一的日志并不能定位问题。 以下罗列出获取Android日志的一些重要手段，部分场景下，Watchdog相关的问题甚至需要以下所有的日志：</p><p>• <strong>logcat</strong> 通过adb logcat命令输出Android的一些当前运行日志，可以通过logcat的 -b 参数指定要输出的日志缓冲区，缓冲区对应着logcat的一种日志类型。 高版本的logcat可以使用 -b all 获取到所有缓冲区的日志</p><ul><li><strong>event</strong> 通过android.util.EventLog工具类打印的日志，一些重要的系统事件会使用此类日志</li><li><strong>main</strong> 通过android.util.Log工具类打印的日志，应用程序，尤其是基于SDK的应用程序，会使用此类日志</li><li><strong>system</strong> 通过android.util.Slog工具类打印的日志，系统相关的日志一般都是使用此类日志，譬如SystemServer</li><li><strong>radio</strong> 通过android.util.Rlog工具类打印的日志，通信模块相关的日志一般都是使用此类日志，譬如RIL<br>• <strong>dumpsys</strong> 通过adb dumpsys命令输出一些重要的系统服务信息，譬如内存、电源、磁盘等， 工作原理可以查阅dumpsys介绍一文</li></ul><p>• <strong>traces</strong> 该文件记录了一个时间段的函数调用栈信息，通常在应用发生ANR(Application Not Responding)时，会触发打印各进程的函数调用栈。 站在Linux的角度，其实就是向进程发送SIGNAL_QUIT(3)请求，譬如，我们可以通过adb shell kill -3 <pid>命令，打印指定进程的的trace。 SIGNAL_QUIT(3)表面意思有一点误导，它其实并不会导致进程退出。输出一般在 <em>/data/anr/traces.txt</em> 文件中，当然，这是可以灵活配置的， Android提供的系统属性dalvik.vm.stack-trace-file可以用来配置生成traces文件的位置。</pid></p><p>• <strong>binder</strong> 通过Binder跨进程调用的日志，可以通过adb shell cat命令从 /proc/binder 下取出对应的日志</p><ul><li><strong>failed_transaction_log</strong></li><li><strong>transaction_log</strong></li><li><strong>transactions</strong></li><li><strong>stats</strong></li></ul><p>• <strong>dropbox</strong> 为了记录历史的logcat日志，Android引入了Dropbox，将历史日志持久化到磁盘中(/data/system/dropbox)。 logcat的缓冲区大小毕竟是有限的，所以需要循环利用，这样历史的日志信息就会被冲掉。在一些自动化测试的场景下，譬如Monkey需要长时间的运行， 就需要把历史的日志全都保存下来。</p><p>• <strong>tombstone</strong> tombstone错误一般由Dalvik错误、native层的代码问题导致的。当系统发生tombstone时，内核会上报一个严重的警告信号， 上层收到后，把当前的调用栈信息持久化到磁盘中(/data/tombstone)</p><p>• <strong>bugreport</strong> 通过adb bugreport命令输出，日志内容多到爆，logcat, traces, dmesg, dumpsys, binder的日志都包含在其中。 由于输出bugreport的时间很长，当系统发生错误时，我们再执行bugreport往往就来不及了(此时，系统可能都已经重启了)，所以，要动用bugreport就需要结合一些其他机制， 譬如在杀掉system_server进程之前，先让bugreport运行完。</p><h2 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h2><p>Watchdog出现的日志很明显，logcat中的event, system中都会有体现，要定位问题，可以从检索日志中的watchdog关键字开始。</p><p>发生Watchdog检测超时这么重要的系统事件，Android会打印一个EventLog：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">watchdog: Blocked in handler XXX    # 表示HandlerChecker超时了</span><br><span class="line">watchdog: Blocked in monitor XXX    # 表示MonitorChecker超时了</span><br></pre></td></tr></table></figure></p><p>Watchdog是运行在system_server进程中，会打印一些System类型的日志。在手机处于非调试状态时，伴随Watchdog出现的往往是system_server进程被杀，从而系统重启。 当Watchdog要主动杀掉system_server进程时，以下关键字就会出现在SystemLog中：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: XXX</span><br><span class="line">Watchdog: XXX</span><br><span class="line">Watchdog: <span class="string">"*** GOODBYE!</span></span><br></pre></td></tr></table></figure></p><p>当我们在日志中检索到上述两类关键信息时，说明“Watchdog显灵”了，从另一个角度来理解，就是“System Not Responding”了。 接下来，我们需要进一步定位在watchdog出现之前，system_server进程在干什么，处于一个什么状态。 这与排除”Application Not Responding“问题差不多，我们需要进程的traces信息、当前系统的CPU运行信息、IO信息。</p><p>找到Watchddog出现之前的traces.txt文件，这个时间差最好不要太大，因为Watchdog默认的超时时间是1分钟，太久以前的traces并不能说明问题。 诱导Watchdong出现的直接原因其实就是system_server中某个线程被阻塞了，这个信息在event和system的log中清晰可见。 我们以一个systemLog为例：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.wm.<span class="function">WindowManagerService on foreground <span class="title">thread</span> <span class="params">(android.fg)</span></span></span><br></pre></td></tr></table></figure></p><p>Watchdog告诉我们Monitor Checker超时了，具体在哪呢？ 名为android.fg的线程在WindowManagerService的monitor()方法被阻塞了。这里隐含了两层意思：</p><ol><li>WindowManagerService实现了Watchdog.Monitor这个接口，并将自己作为Monitor Checker的对象加入到了Watchdog的监测集中</li><li>monitor()方法是运行在android.fg线程中的。Android将android.fg设计为一个全局共享的线程，意味着它的消息队列可以被其他线程共享， Watchdog的Monitor Checker就是使用的android.fg线程的消息队列。因此，出现Monitor Checker的超时，肯定是android.fg线程阻塞在monitor()方法上。</li></ol><p>我们打开system_server进程的traces，检索 android.fg 可以快速定位到该线程的函数调用栈：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"android.fg"</span> prio=<span class="number">5</span> tid=<span class="number">25</span> Blocked</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x12eef900</span> self=<span class="number">0x7f7a8b1000</span></span><br><span class="line">  | sysTid=<span class="number">973</span> nice=<span class="number">0</span> cgrp=<span class="keyword">default</span> sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f644e9000</span></span><br><span class="line">  | state=S schedstat=( <span class="number">3181688530</span> <span class="number">2206454929</span> <span class="number">8991</span> ) utm=<span class="number">251</span> stm=<span class="number">67</span> core=<span class="number">1</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7f643e7000</span>-<span class="number">0x7f643e9000</span> stackSize=<span class="number">1036</span>KB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  at com.android.server.wm.WindowManagerService.monitor(WindowManagerService.java:<span class="number">13125</span>)</span><br><span class="line">  - waiting to lock &lt;<span class="number">0x126dccb8</span>&gt; (a java.util.HashMap) held by thread <span class="number">91</span></span><br><span class="line">  at com.android.server.Watchdog$HandlerChecker.run(Watchdog.java:<span class="number">204</span>)</span><br><span class="line">  at android.os.Handler.handleCallback(Handler.java:<span class="number">815</span>)</span><br><span class="line">  at android.os.Handler.dispatchMessage(Handler.java:<span class="number">104</span>)</span><br><span class="line">  at android.os.Looper.loop(Looper.java:<span class="number">194</span>)</span><br><span class="line">  at android.os.HandlerThread.run(HandlerThread.java:<span class="number">61</span>)</span><br><span class="line">  at com.android.server.ServiceThread.run(ServiceThread.java:<span class="number">46</span>)</span><br></pre></td></tr></table></figure></p><p>android.fg线程调用栈告诉我们几个关键的信息：</p><ul><li>这个线程当前的状态是Blocked，阻塞</li><li>由Watchdog发起调用monitor()，这是一个Watchdog检查，阻塞已经超时</li><li>waiting to lock <0x126dccb8>： 阻塞的原因是monitor()方法中在等锁<0x126dccb8></0x126dccb8></0x126dccb8></li><li>held by thread 91： 这个锁被编号为91的线程持有，需要进一步观察91号线程的状态。</li></ul><blockquote><p>题外话：每一个进程都会对自己所辖的线程编号，从1开始。1号线程通常就是我们所说的主线程。 线程在Linux系统中还有一个全局的编号，由sysTid表示。我们在logcat等日志中看到的一般是线程的全局编号。 譬如，本例中android.fg线程在system_server进程中的编号是25，系统全局编号是973。</p></blockquote><p>可以在traces.txt文件中检索 tid=91 来快速找到91号线程的函数调用栈信息：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"Binder_C"</span> prio=<span class="number">5</span> tid=<span class="number">91</span> Native</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x12e540a0</span> self=<span class="number">0x7f63289000</span></span><br><span class="line">  | sysTid=<span class="number">1736</span> nice=<span class="number">0</span> cgrp=<span class="keyword">default</span> sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f6127c000</span></span><br><span class="line">  | state=S schedstat=( <span class="number">96931835222</span> <span class="number">49673449591</span> <span class="number">260122</span> ) utm=<span class="number">7046</span> stm=<span class="number">2647</span> core=<span class="number">2</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7f5ffbc000</span>-<span class="number">0x7f5ffbe000</span> stackSize=<span class="number">1008</span>KB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  at libcore.io.Posix.writeBytes(Native method)</span><br><span class="line">  at libcore.io.Posix.write(Posix.java:<span class="number">258</span>)</span><br><span class="line">  at libcore.io.BlockGuardOs.write(BlockGuardOs.java:<span class="number">313</span>)</span><br><span class="line">  at libcore.io.IoBridge.write(IoBridge.java:<span class="number">537</span>)</span><br><span class="line">  at java.io.FileOutputStream.write(FileOutputStream.java:<span class="number">186</span>)</span><br><span class="line">  at com.android.internal.util.FastPrintWriter.flushBytesLocked(FastPrintWriter.java:<span class="number">334</span>)</span><br><span class="line">  at com.android.internal.util.FastPrintWriter.flushLocked(FastPrintWriter.java:<span class="number">355</span>)</span><br><span class="line">  at com.android.internal.util.FastPrintWriter.appendLocked(FastPrintWriter.java:<span class="number">303</span>)</span><br><span class="line">  at com.android.internal.util.FastPrintWriter.print(FastPrintWriter.java:<span class="number">466</span>)</span><br><span class="line">  - locked &lt;<span class="meta">@addr</span>=<span class="number">0x134c4910</span>&gt; (a com.android.internal.util.FastPrintWriter$DummyWriter)</span><br><span class="line">  at com.android.server.wm.WindowState.dump(WindowState.java:<span class="number">1510</span>)</span><br><span class="line">  at com.android.server.wm.WindowManagerService.dumpWindowsNoHeaderLocked(WindowManagerService.java:<span class="number">12279</span>)</span><br><span class="line">  at com.android.server.wm.WindowManagerService.dumpWindowsLocked(WindowManagerService.java:<span class="number">12266</span>)</span><br><span class="line">  at com.android.server.wm.WindowManagerService.dump(WindowManagerService.java:<span class="number">12654</span>)</span><br><span class="line">  - locked &lt;<span class="number">0x126dccb8</span>&gt; (a java.util.HashMap)</span><br><span class="line">  at android.os.Binder.dump(Binder.java:<span class="number">324</span>)</span><br><span class="line">  at android.os.Binder.onTransact(Binder.java:<span class="number">290</span>)</span><br></pre></td></tr></table></figure></p><p>91号线程的名字是Binder_C，它的函数调用栈告诉我们几个关键信息：</p><ul><li>Native，表示线程处于运行状态(RUNNING)，并且正在执行JNI方法</li><li>在WindowManagerService.dump()方法申请了锁<0x126dccb8>，这个锁正是android.fg线程所等待的</0x126dccb8></li><li>FileOutputStream.write()表示Binder_C线程在执行IO写操作，正式因为这个写操作一直在阻塞，导致线程持有的锁不能释放</li></ul><blockquote><p>题外话：关于Binder线程。当Android进程启动时，就会创建一个线程池，专门处理Binder事务。线程池中会根据当前的binder线程计数器的值来构造新创建的binder线程, 线程名”Binder_%X”，X是十六进制。当然，线程池的线程数也有上限，默认情况下为16，所以，可以看到 Binder_1 ~ Binder_F 这样的线程命名。</p></blockquote><p>聪明的你看到这或许已经能够想到解决办法了，在这个IO写操作上加一个超时机制，并且这个超时小于Watchdog的超时，不就可以让线程释放它所占有的锁了吗？ 是的，这确实可以作为一个临时解决方案(Workaround)，或者说一个保护机制。但我们可以再往深处想一想，这个IO写操作为什么会阻塞：</p><ul><li>是不是IO缓冲区满了，导致写阻塞呢？</li><li>是不是写操作有什么锁，导致这个write方法在等锁呢？</li><li>是不是当前系统的IO负载过于高，导致写操作效率很低呢？</li></ul><p>这都需要我们再进一步从日志中去找原因。如果已有的日志不全，找不到论据，我们还需要设计场景来验证假设，解决问题的难度陡然上升。</p><h2 id="场景还原"><a href="#场景还原" class="headerlink" title="场景还原"></a>场景还原</h2><p>我们经历了两个关键步骤：</p><ol><li>通过event或system类型的日志，发现了Watchdog杀掉system_server导致系统重启</li><li>通过traces日志，发了导致Watchdog出现的具体线程操作</li></ol><p>这两个过程基本就涵盖了Watchdog的运行机制了，但这并没有解决问题啊。我们需要找到线程阻塞的原因是什么，然而，线程阻塞的原因就千奇百怪了。 如果有问题出现的现场，并且问题可以重现，那么我们可以通过调试的手段来分析问题产生的原因。 如果问题只是偶然出现，甚至只有一堆日志，我们就需要从日志中来还原问题出现的场景，这一步才是真正考验大家Android/Linux功底的地方。</p><p>继续以上述问题为例，我们来进一步还原问题出现的场景，从Java层的函数调用栈来看：</p><ul><li>首先，跨进程发起了Binder.dump()方法的调用：at android.os.Binder.dump(Binder.java:324)</li><li>然后，进入了WMS的dump()：at com.android.server.wm.WindowManagerService.dump(WindowManagerService.java:12654)</li><li>接着，发生了写文件操作：at java.io.FileOutputStream.write(FileOutputStream.java:186)</li><li>最后，调用了JNI方法：at libcore.io.Posix.writeBytes(Native method)</li></ul><p>Binder_C线程要出现这种函数调用栈，我们可以初步确定是Android接受了如下命令:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ adb shell dumpsys window</span><br></pre></td></tr></table></figure></p><p>当通过命令行运行以上命令时，客户端(PC)的adb server会向服务端(手机)的adbd发送指令， adbd进程会fork出一个叫做dumpsys的子进程，dumpsys进程再利用Binder机制和system_server通信</p><p>仅凭这个还是分析不出问题所在，我们需要启用内核的日志了。当调用JNI方法libcore.io.Posix.writeBytes()时，会触发系统调用， Linux会从用户态切换到内核态，内核的函数调用栈也可以从traces中找到：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">kernel: __switch_to+<span class="number">0x74</span>/<span class="number">0x8c</span></span><br><span class="line">kernel: pipe_wait+<span class="number">0x60</span>/<span class="number">0x9c</span></span><br><span class="line">kernel: pipe_write+<span class="number">0x278</span>/<span class="number">0x5cc</span></span><br><span class="line">kernel: do_sync_write+<span class="number">0x90</span>/<span class="number">0xcc</span></span><br><span class="line">kernel: vfs_write+<span class="number">0xa4</span>/<span class="number">0x194</span></span><br><span class="line">kernel: SyS_write+<span class="number">0x40</span>/<span class="number">0x8c</span></span><br><span class="line">kernel: cpu_switch_to+<span class="number">0x48</span>/<span class="number">0x4c</span></span><br></pre></td></tr></table></figure></p><p>在Java层，明确指明要写文件(FileOutputStream)，正常情况下，系统调用write()就完事了，但Kernel却打开了一个管道，最终阻塞在了pipe_wait()方法。 什么场景下会打开一个管道，而且管道会阻塞呢？一系列的猜想和验证过程接踵而至。</p><p>这里有必要先补充一些基础知识了：</p><blockquote><p><a href="http://www.cnblogs.com/biyeymyhjob/archive/2012/11/03/2751593.html" target="_blank" rel="noopener">Linux进程间通信之管道(pipe)</a><br>   Linux的管道实现借助了文件系统的file结构和VFS(Virtual File System)，通过将两个file结构指向同一个临时的VFS索引节点，而这个VFS索引节点又指向一个物理页面时， 实际上就建立了一个管道。<br>这就解释了为什么发起系统调用write的时候，打开了一个管道。因为dumpsys和system_server进程，将自己的file结构指向了同一个VFS索引节点。</p></blockquote><blockquote><p><a href="https://blog.csdn.net/sj13051180/article/details/47865803" target="_blank" rel="noopener">管道挂起的案例</a><br>  管道是一个生产者-消费者模型，当缓冲区满时，则生产者不能往管道中再写数据了，需等到消费者读数据。如果消费者来不及处理缓冲区的数据，或者锁定缓冲区，则生产者就挂起了。<br>结合到例子中的场景，system_server进程无法往管道中写数据，很可能是dumpsys进程一直忙碌来不及处理新的数据。</p></blockquote><p>接下来，需要再从日志中寻找dumpsys进程的运行状态了：</p><ul><li>是不是dumpsys进程的负载太高？</li><li>是不是dumpsys进程死掉了，导致一直没有处理缓冲区数据？</li><li>是不是dumpsys进程有死锁？</li></ul><p>接下来的分析过程已经偏离Watchdog机制越来越远了，我们点到为止。</p><p>小伙伴们可以看到，场景还原涉及到的知识点非常之宽泛，而且有一定的深度。在没有现场的情况下，伴随一系列的假设和验证过程，充满了不确定性和发现问题的喜悦。 正所谓，同问题做斗争，其乐无穷！</p><p>至此，我们分析Watchdog问题的惯用方法，回答前面提出来的第二个问题：<br>通过event或system类型的logcat日志，检索Watchdog出现的关键信息；通过traces，分析出导致Watchdog检查超时的直接原因；通过其他日志，还原出问题出现的场景。</p><h1 id="实例分析"><a href="#实例分析" class="headerlink" title="实例分析"></a>实例分析</h1><p>在上面介绍Watchdog问题分析方法的时候，我们其实已经举了一个例子。通常，比较容易定位导致Watchdog出现的直接原因(Direct Cause)，但很难找到更深层次的原因(Root Cause)。 这个小节，我们再介绍一个实例，来分析Watchdog出现的另一种场景。诚然，仅凭几个例子，远不够涵盖Watchdog的所有问题，我们的章法还是按照一定的方法论来深究问题。</p><p>回顾一下解决问题三部曲：</p><ol><li>日志获取。日志种类繁多，分析Watchdog问题，宁滥毋缺</li><li>问题定位。从logcat中锁定watchdog的出现，从traces锁定直接原因</li><li>场景还原。结合各类日志，不断假设验证</li></ol><p>以CPU占用过高的场景为例：<br>从sys_log中，检索到了Watchdog的出现关键信息</p><blockquote><p>TIPS: 在sys_log中搜索关键字”WATCHDOG KILLING SYSTEM PROCESS”<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">51.548</span>   <span class="number">892</span>  <span class="number">1403</span> W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: <span class="function">Blocked in handler on <span class="title">ActivityManager</span> <span class="params">(ActivityManager)</span></span></span><br></pre></td></tr></table></figure></p></blockquote><p>这是一个Watchdog的Looper Checker超时，由于ActivityManager这个线程一直处于忙碌状态，导致Watchdog检查超时。 Watchdog出现的时间是10-14 17:10:51.548左右，需要从traces.txt中找到这个时间段的system_server进程的函数调用栈信息， system_server的进程号是892。</p><p>从traces.txt中找到对应的函数调用栈</p><p>traces.txt包含很多进程在不同时间段的函数调用栈信息，为了检索的方便，首先可以将traces.txt分块。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"ActivityManager"</span> prio=<span class="number">5</span> tid=<span class="number">17</span> TimedWaiting</span><br><span class="line">  | group=<span class="string">"main"</span> sCount=<span class="number">1</span> dsCount=<span class="number">0</span> obj=<span class="number">0x12c0e6d0</span> self=<span class="number">0x7f84caf000</span></span><br><span class="line">  | sysTid=<span class="number">938</span> nice=-<span class="number">2</span> cgrp=<span class="keyword">default</span> sched=<span class="number">0</span>/<span class="number">0</span> handle=<span class="number">0x7f7d887000</span></span><br><span class="line">  | state=S schedstat=( <span class="number">107864628645</span> <span class="number">628257779012</span> <span class="number">60356</span> ) utm=<span class="number">7799</span> stm=<span class="number">2987</span> core=<span class="number">2</span> HZ=<span class="number">100</span></span><br><span class="line">  | stack=<span class="number">0x7f6e68f000</span>-<span class="number">0x7f6e691000</span> stackSize=<span class="number">1036</span>KB</span><br><span class="line">  | held mutexes=</span><br><span class="line">  at java.lang.Object.wait!(Native method)</span><br><span class="line">  - waiting on &lt;<span class="number">0x264ff09d</span>&gt; (a com.android.server.am.ActivityManagerService$<span class="number">5</span>)</span><br><span class="line">  at java.lang.Object.wait(Object.java:<span class="number">422</span>)</span><br><span class="line">  at com.android.server.am.ActivityManagerService.dumpStackTraces(ActivityManagerService.java:<span class="number">5395</span>)</span><br><span class="line">  at com.android.server.am.ActivityManagerService.dumpStackTraces(ActivityManagerService.java:<span class="number">5282</span>)</span><br><span class="line">  at com.android.server.am.ActivityManagerService$AnrActivityManagerService.dumpStackTraces(ActivityManagerService.java:<span class="number">22676</span>)</span><br><span class="line">  at com.mediatek.anrmanager.ANRManager$AnrDumpMgr.dumpAnrDebugInfoLocked(SourceFile:<span class="number">1023</span>)</span><br><span class="line">  at com.mediatek.anrmanager.ANRManager$AnrDumpMgr.dumpAnrDebugInfo(SourceFile:<span class="number">881</span>)</span><br><span class="line">  at com.android.server.am.ActivityManagerService.appNotResponding(ActivityManagerService.java:<span class="number">6122</span>)</span><br><span class="line">  - locked &lt;<span class="number">0x21c77912</span>&gt; (a com.mediatek.anrmanager.ANRManager$AnrDumpRecord)</span><br><span class="line">  at com.android.server.am.BroadcastQueue$AppNotResponding.run(BroadcastQueue.java:<span class="number">228</span>)</span><br><span class="line">  at android.os.Handler.handleCallback(Handler.java:<span class="number">815</span>)</span><br><span class="line">  at android.os.Handler.dispatchMessage(Handler.java:<span class="number">104</span>)</span><br><span class="line">  at android.os.Looper.loop(Looper.java:<span class="number">192</span>)</span><br><span class="line">  at android.os.HandlerThread.run(HandlerThread.java:<span class="number">61</span>)</span><br><span class="line">  at com.android.server.ServiceThread.run(ServiceThread.java:<span class="number">46</span>)</span><br></pre></td></tr></table></figure></p><p>ActivityManager线程实际上运行着AMS的消息队列，这个函数调用栈的关键信息：</p><ul><li>线程状态为TimedWaiting, 这表示当前线程阻塞在一个超时的wait()方法</li><li>正在处理广播消息超时发生的ANR(Application Not Responding)，需要将当前的函数调用栈打印出来</li><li>最终在<0x264ff09d>等待，可以从AMS的源码 中找到这一处锁的源码，因为dumpStackTraces()会写文件，所以AMS设计了一个200毫秒的超时锁。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">observer.wait(<span class="number">200</span>);  <span class="comment">// Wait for write-close, give up after 200msec</span></span><br></pre></td></tr></table></figure></0x264ff09d></li></ul><p>还原问题的场景<br>从ActivityManager这个线程的调用栈，我们就会有一些疑惑：</p><ul><li>是哪个应用发生了ANR？为什么会发生ANR？</li><li>超时锁只用200毫秒就释放了，为什么会导致Watchdog检查超时？(AMS的Looper默认超时是1分钟)<br>带着这些疑惑，我们再回到日志中：</li></ul><p>从sys_log中，可以检索到Watchdog出现的时间点(17:10:51.548)之前，com.android.systemui发生了ANR，从而引发AMS打印函数调用栈:</p><blockquote><p>TIPS: 在sys_log中检索”ANR in”关键字或在event_log中检索”anr”关键字<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager: ANR in com.android.systemui, time=<span class="number">27097912</span></span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager: Reason: Broadcast of Intent &#123; act=android.intent.action.TIME_TICK flg=<span class="number">0x50000114</span> (has extras) &#125;</span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager: Load: <span class="number">89.22</span> / <span class="number">288.15</span> / <span class="number">201.91</span></span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager: Android time :[<span class="number">2015</span>-<span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.14</span>] [<span class="number">27280.396</span>]</span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager: CPU usage from <span class="number">17016</span>ms to <span class="number">0</span>ms ago:</span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager:   <span class="number">358</span>% <span class="number">23682</span>/float_bessel: <span class="number">358</span>% user + <span class="number">0</span>% kernel</span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager:   <span class="number">57</span>% <span class="number">23604</span>/debuggerd64: <span class="number">3.8</span>% user + <span class="number">53</span>% kernel / faults: <span class="number">11369</span> minor</span><br><span class="line"><span class="number">10</span>-<span class="number">14</span> <span class="number">17</span>:<span class="number">10</span>:<span class="number">04.215</span>   <span class="number">892</span>   <span class="number">938</span> E ANRManager:   <span class="number">2</span>% <span class="number">892</span>/system_server: <span class="number">0.9</span>% user + <span class="number">1</span>% kernel / faults: <span class="number">136</span> minor</span><br></pre></td></tr></table></figure></p></blockquote><p>从这个日志信息中，我们两个疑惑就释然了：</p><p>发生ANR之前的CPU负载远高于正常情况好几倍(Load： 89.22 / 288.15 / 201.91)，在这种CPU负载下，com.android.systemui进程发生处理广播消息超时(Reason: Broadcast of Intent)再正常不过了。 在这之前CPU都被float_bessel这个进程给占了，这货仅凭一己之力就耗了358%的CPU资源。</p><p>observer.wait(200)在调用后，便进入排队等待唤醒状态(Waiting)，在等待200毫秒后，便重新开始申请CPU资源，而此时，CPU资源一直被float_bessel占着没有释放，所以该线程一直在等CPU资源。 等了1分钟后，Watchdog跳出来说“不行，你已经等了1分钟了，handler处理其他消息了”。</p><p>在多核情况下，CPU的使用率统计会累加多个核的使用率，所以会出现超过100%的情况。那么float_bessel究竟是什么呢？它是一个Linux的测试样本，贝塞尔函数的计算，耗的就是CPU。</p><p>这样，该问题的场景我们就还原出来了：在压力测试的环境下，CPU被float_bessel运算占用，导致com.android.systemui进程发生ANR，从而引发AMS打印trace; 但由于AMS一直等不到CPU资源，Watchdog检测超时，杀掉system_server进程，系统重启。</p><p>对于压力测试而言，我们一般会设定一个通过标准，在某些压力情况下，出现一些错误是允许的。对于Android实际用户的使用场景而言，本例中的压力通常是不存在的，所以在实际项目中，这种类型的Watchdog问题，我们一般不解决。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>Android中Watchdog用来看护system_server进程，system_server进程运行着系统最终要的服务，譬如AMS、PKMS、WMS等， 当这些服务不能正常运转时，Watchdog可能会杀掉system_server，让系统重启。</p><p>Watchdog的实现利用了锁和消息队列机制。当system_server发生死锁或消息队列一直处于忙碌状态时，则认为系统已经没有响应了(System Not Responding)。</p><p>在分析Watchdog问题的时候，首先要有详尽的日志，其次要能定位出导致Watchdog超时的直接原因，最重要的是能还原出问题发生的场景。</p><!-- 可以用来注释，参与编译，在Html文档中会有这句话，但是不会显示 ^-^ --><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://duanqz.github.io/2015-10-12-Watchdog-Analysis#section-2" target="_blank" rel="noopener">Watchdog机制以及问题分析</a><br><a href="https://blog.csdn.net/fu_kevin0606/article/details/64479489" target="_blank" rel="noopener">Android7.0 Watchdog机制</a><br><a href="https://blog.csdn.net/xichangbao/article/details/76727425" target="_blank" rel="noopener">Android系统高通平台Kernel Watchdog</a><br><a href="https://blog.csdn.net/yangwen123/article/details/11264461" target="_blank" rel="noopener">Android软Watchdog源码分析</a></p><h1 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h1><p><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/Watchdog.java" target="_blank" rel="noopener">Watchdog.java</a> </p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概览&quot;&gt;&lt;a href=&quot;#概览&quot; class=&quot;headerlink&quot; title=&quot;概览&quot;&gt;&lt;/a&gt;概览&lt;/h1&gt;&lt;p&gt;Watchdog的中文的“看门狗”，有保护的意思。最早引入Watchdog是在单片机系统中，由于单片机的工作环境容易受到外界磁场的干扰，导致
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Watchdog" scheme="http://yoursite.com/tags/Watchdog/"/>
    
  </entry>
  
  <entry>
    <title>Android中Window机制</title>
    <link href="http://yoursite.com/Android-Window-Mechanism/"/>
    <id>http://yoursite.com/Android-Window-Mechanism/</id>
    <published>2018-04-03T03:23:43.000Z</published>
    <updated>2018-09-03T09:16:03.511Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Window的概念"><a href="#Window的概念" class="headerlink" title="Window的概念"></a>Window的概念</h1><p>Android手机中所有的视图都是通过Window来呈现的，像常用的Activity，Dialog，PopupWindow，Toast，他们的视图都是附加在Window上的，所以可以这么说 ——「Window是View的直接管理者。」<br><img src="/Android-Window-Mechanism/window-01.png" alt="&quot;Window的界面解析&quot;"></p><p>Android的窗口管理子系统架构如下：<br><img src="/Android-Window-Mechanism/window-011.png" alt="&quot;窗口管理子系统&quot;"></p><h2 id="Window"><a href="#Window" class="headerlink" title="Window"></a>Window</h2><p> <a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/Window.java" target="_blank" rel="noopener">Window.java</a> 是一个顶级窗口查看和行为的一个抽象基类。这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法，比如添加背景，标题等等。当你需要用到Window的时候，你应该使用它的唯一实现类PhoneWindow<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Abstract base class for a top-level window look and behavior policy.  An</span></span><br><span class="line"><span class="comment"> * instance of this class should be used as the top-level view added to the</span></span><br><span class="line"><span class="comment"> * window manager. It provides standard UI policies such as a background, title</span></span><br><span class="line"><span class="comment"> * area, default key processing, etc.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;The only existing implementation of this abstract class is</span></span><br><span class="line"><span class="comment"> * android.view.PhoneWindow, which you should instantiate when needing a</span></span><br><span class="line"><span class="comment"> * Window.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Window</span> </span>&#123;</span><br><span class="line">    <span class="comment">/** Flag for the "options panel" feature.  This is enabled by default. */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FEATURE_OPTIONS_PANEL = <span class="number">0</span>;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Callback</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Called to process key events.  At the very least your</span></span><br><span class="line"><span class="comment">         * implementation must call</span></span><br><span class="line"><span class="comment">         * &#123;<span class="doctag">@link</span> android.view.Window#superDispatchKeyEvent&#125; to do the</span></span><br><span class="line"><span class="comment">         * standard key processing.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> event The key event.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@return</span> boolean Return true if this event was consumed.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">dispatchKeyEvent</span><span class="params">(KeyEvent event)</span></span>;</span><br><span class="line">    ....</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> View <span class="title">getDecorView</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> View <span class="title">findViewById</span><span class="params">(@IdRes <span class="keyword">int</span> id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> getDecorView().findViewById(id);</span><br><span class="line">    &#125; </span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(View view)</span></span>;</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure></p><h2 id="PhoneWindow"><a href="#PhoneWindow" class="headerlink" title="PhoneWindow"></a>PhoneWindow</h2><p> <a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/policy/PhoneWindow.java" target="_blank" rel="noopener">PhoneWindow.java</a><br> 每一个Activity都包含一个Window对象，而Window是一个抽象类，具体实现是PhoneWindow。在Activity中的setContentView实际上是调用PhoneWindow的setContentView方法。并且PhoneWindow中包含着成员变量DecorView。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">**</span><br><span class="line"> * Android-specific Window.</span><br><span class="line"> * &lt;p&gt;</span><br><span class="line"> * todo: need to pull the generic functionality out into a base <span class="class"><span class="keyword">class</span></span></span><br><span class="line"><span class="class"> * <span class="title">in</span> <span class="title">android</span>.<span class="title">widget</span>.</span></span><br><span class="line"><span class="class"> *</span></span><br><span class="line"><span class="class"> * @<span class="title">hide</span></span></span><br><span class="line"><span class="class"> */</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">PhoneWindow</span> <span class="keyword">extends</span> <span class="title">Window</span> <span class="keyword">implements</span> <span class="title">MenuBuilder</span>.<span class="title">Callback</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String TAG = <span class="string">"PhoneWindow"</span>;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">// This is the top-level view of the window, containing the window decor.</span></span><br><span class="line">    <span class="keyword">private</span> DecorView mDecor;</span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(<span class="keyword">int</span> layoutResID)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window</span></span><br><span class="line">        <span class="comment">// decor, when theme attributes and the like are crystalized. Do not check the feature</span></span><br><span class="line">        <span class="comment">// before this happens.</span></span><br><span class="line">        <span class="keyword">if</span> (mContentParent == <span class="keyword">null</span>) &#123;</span><br><span class="line">            installDecor();</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            mContentParent.removeAllViews();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            <span class="keyword">final</span> Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,</span><br><span class="line">                    getContext());</span><br><span class="line">            transitionTo(newScene);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mLayoutInflater.inflate(layoutResID, mContentParent);</span><br><span class="line">        &#125;</span><br><span class="line">        mContentParent.requestApplyInsets();</span><br><span class="line">        <span class="keyword">final</span> Callback cb = getCallback();</span><br><span class="line">        <span class="keyword">if</span> (cb != <span class="keyword">null</span> &amp;&amp; !isDestroyed()) &#123;</span><br><span class="line">            cb.onContentChanged();</span><br><span class="line">        &#125;</span><br><span class="line">        mContentParentExplicitlySet = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>我们来看看Activity里面的setContentView方法的实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Activity</span> <span class="keyword">extends</span> <span class="title">ContextThemeWrapper</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">LayoutInflater</span>.<span class="title">Factory2</span>,</span></span><br><span class="line"><span class="class">        <span class="title">Window</span>.<span class="title">Callback</span>, <span class="title">KeyEvent</span>.<span class="title">Callback</span>,</span></span><br><span class="line"><span class="class">        <span class="title">OnCreateContextMenuListener</span>, <span class="title">ComponentCallbacks2</span>,</span></span><br><span class="line"><span class="class">        <span class="title">Window</span>.<span class="title">OnWindowDismissedCallback</span>, <span class="title">WindowControllerCallback</span>,</span></span><br><span class="line"><span class="class">        <span class="title">AutofillManager</span>.<span class="title">AutofillClient</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"Activity"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> DEBUG_LIFECYCLE = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Set the activity content from a layout resource.  The resource will be</span></span><br><span class="line"><span class="comment">     * inflated, adding all top-level views to the activity.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> layoutResID Resource ID to be inflated.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> #setContentView(android.view.View)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(@LayoutRes <span class="keyword">int</span> layoutResID)</span> </span>&#123;</span><br><span class="line">        getWindow().setContentView(layoutResID);</span><br><span class="line">        initWindowDecorActionBar();</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//getWindow获取到的是mWindow         </span></span><br><span class="line"><span class="comment">//在attach方法里,mWindow = new PhoneWindow(this, window);</span></span><br></pre></td></tr></table></figure></p><h2 id="DecorView"><a href="#DecorView" class="headerlink" title="DecorView"></a>DecorView</h2><p> <a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/policy/DecorView.java" target="_blank" rel="noopener">DecorView.java</a><br> 作为顶级View,DecorView一般情况下它内部会包含一个竖直方向的LinearLayout，上面的标题栏(titleBar)，下面是内容栏。通常我们在Activity中通过setContentView所设置的布局文件就是被加载到id为android.R.id.content的内容栏里(FrameLayout)<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DecorView</span> <span class="keyword">extends</span> <span class="title">FrameLayout</span> <span class="keyword">implements</span> <span class="title">RootViewSurfaceTaker</span>, <span class="title">WindowCallbacks</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"DecorView"</span>;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="setContentView"><a href="#setContentView" class="headerlink" title="setContentView"></a>setContentView</h2><p> setContentView调用流程分析<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//PhoneWindow.java</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(<span class="keyword">int</span> layoutResID)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mContentParent == <span class="keyword">null</span>) &#123;</span><br><span class="line"><span class="comment">//①初始化</span></span><br><span class="line">        <span class="comment">//创建DecorView对象和mContentParent对象 </span></span><br><span class="line">            installDecor();</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            mContentParent.removeAllViews();<span class="comment">//Activity转场动画相关</span></span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//②填充Layout</span></span><br><span class="line">        <span class="keyword">if</span> (hasFeature(FEATURE_CONTENT_TRANSITIONS)) &#123;</span><br><span class="line">            <span class="keyword">final</span> Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,</span><br><span class="line">                    getContext());</span><br><span class="line">            transitionTo(newScene);<span class="comment">//Activity转场动画相关</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//将Activity设置的布局文件，加载到mContentParent中</span></span><br><span class="line">            mLayoutInflater.inflate(layoutResID, mContentParent);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//让DecorView的内容区域延伸到systemUi下方，防止在扩展时被覆盖，达到全屏、沉浸等不同体验效果。</span></span><br><span class="line">        mContentParent.requestApplyInsets();</span><br><span class="line"><span class="comment">//③通知Activity布局改变</span></span><br><span class="line">        <span class="keyword">final</span> Callback cb = getCallback();</span><br><span class="line">        <span class="keyword">if</span> (cb != <span class="keyword">null</span> &amp;&amp; !isDestroyed()) &#123;</span><br><span class="line">        <span class="comment">//触发Activity的onContentChanged方法  </span></span><br><span class="line">            cb.onContentChanged();</span><br><span class="line">        &#125;</span><br><span class="line">        mContentParentExplicitlySet = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>• ①初始化 ： Activity第一次调用setContentView，则会调用installDecor()方法创建DecorView对象和mContentParent对象。FEATURE_CONTENT_TRANSITIONS表示是否使用转场动画。如果内容已经加载过，并且不需要动画，则会调用removeAllViews移除内容以便重新填充Layout。</p><p>• ②填充Layout ： 初始化完毕，如果设置了FEATURE_CONTENT_TRANSITIONS，就会创建Scene完成转场动画。否则使用布局填充器将布局文件填充至mContentParent。到此为止，Activity的布局文件已经添加到DecorView里面了，所以可以理解Activity的setContentView方法的由来，因为布局文件是添加到DecorView的mContentParent中，所以方法名为setContentView无可厚非。「开头第一张图的contentView」</p><h2 id="installDecor"><a href="#installDecor" class="headerlink" title="installDecor"></a>installDecor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//PhoneWindow  --&gt; setContentView()</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">installDecor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mDecor == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//调用该方法创建new一个DecorView</span></span><br><span class="line">            mDecor = generateDecor();</span><br><span class="line">        ......</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            mDecor.setWindow(<span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (mContentParent == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//根据主题theme设置对应的xml布局文件以及Feature(包括style,layout,转场动画,属性等)到DecorView中。</span></span><br><span class="line">        <span class="comment">//并将mContentParent绑定至id为ID_ANDROID_CONTENT(com.android.internal.R.id.content)的ViewGroup</span></span><br><span class="line">        <span class="comment">//mContentParent在DecorView添加的xml文件中</span></span><br><span class="line">            mContentParent = generateLayout(mDecor);</span><br><span class="line">            <span class="comment">// Set up decor part of UI to ignore fitsSystemWindows if appropriate.</span></span><br><span class="line">            mDecor.makeOptionalFitsSystemWindows();</span><br><span class="line">                ......</span><br><span class="line">            <span class="comment">//添加其他资源</span></span><br><span class="line">            <span class="comment">//设置转场动画</span></span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="generateLayout"><a href="#generateLayout" class="headerlink" title="generateLayout"></a>generateLayout</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//PhoneWindow --&gt; setContentView()  --&gt;installDecor() </span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> ViewGroup <span class="title">generateLayout</span><span class="params">(DecorView decor)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Apply data from current theme.</span></span><br><span class="line">        <span class="comment">//获取当前的主题，加载默认资源和布局</span></span><br><span class="line">        TypedArray a = getWindowStyle();</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//根据theme的设定，找到对应的Feature(包括style,layout,转场动画，属性等)</span></span><br><span class="line">        <span class="keyword">if</span> (a.getBoolean(R.styleable.Window_windowNoTitle, <span class="keyword">false</span>)) &#123;</span><br><span class="line">            requestFeature(FEATURE_NO_TITLE);<span class="comment">//无titleBar</span></span><br><span class="line">        &#125; </span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">if</span> (a.getBoolean(R.styleable.Window_windowFullscreen, <span class="keyword">false</span>)) &#123;</span><br><span class="line">            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN &amp; (~getForcedWindowFlags()));<span class="comment">//设置全屏</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (a.getBoolean(R.styleable.Window_windowTranslucentStatus,</span><br><span class="line">                <span class="keyword">false</span>)) &#123;</span><br><span class="line">            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS</span><br><span class="line">                    &amp; (~getForcedWindowFlags()));<span class="comment">//透明状态栏</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//根据当前主题，设定不同的Feature</span></span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">int</span> layoutResource;</span><br><span class="line">        <span class="keyword">int</span> features = getLocalFeatures();</span><br><span class="line">        <span class="comment">//由于布局较多，我们拿有titleBar的例子来看</span></span><br><span class="line">        <span class="keyword">if</span> ((features &amp; (<span class="number">1</span> &lt;&lt; FEATURE_NO_TITLE)) == <span class="number">0</span>) &#123;</span><br><span class="line">            ......</span><br><span class="line">                layoutResource = R.layout.screen_title;</span><br><span class="line">        &#125; </span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">else</span> &#123;<span class="comment">//无titleBar</span></span><br><span class="line">            layoutResource = R.layout.screen_simple;</span><br><span class="line">        &#125;</span><br><span class="line">        mDecor.startChanging();</span><br><span class="line">        <span class="comment">//将布局layout，添加至DecorView中</span></span><br><span class="line">        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);</span><br><span class="line">        <span class="comment">//从布局中获取`ID_ANDROID_CONTENT`，并关联至contentParent</span></span><br><span class="line">        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//配置完成，DecorView根据已有属性调整布局状态</span></span><br><span class="line">        mDecor.finishChanging();</span><br><span class="line">        <span class="keyword">return</span> contentParent;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">//sdk\platforms\android-25\data\res\layout\screen_simple.xml</span><br><span class="line"><span class="tag">&lt;<span class="name">LinearLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:fitsSystemWindows</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:orientation</span>=<span class="string">"vertical"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ViewStub</span> <span class="attr">android:id</span>=<span class="string">"@+id/action_mode_bar_stub"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:inflatedId</span>=<span class="string">"@+id/action_mode_bar"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout</span>=<span class="string">"@layout/action_mode_bar"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:theme</span>=<span class="string">"?attr/actionBarTheme"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:id</span>=<span class="string">"@android:id/content"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:foregroundInsidePadding</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:foregroundGravity</span>=<span class="string">"fill_horizontal|top"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">android:foreground</span>=<span class="string">"?android:attr/windowContentOverlay"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">LinearLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><p>首先根据当前的主题，加载默认资源和布局，根据相关的FEATURE获取资源布局layout文件。然后将布局添加到DecorView中，并且将contentParent与布局中id为ID_ANDROID_CONTENT的FrameLayout绑定。所以我们可以通过findViewById(com.android.internal.R.id.content)获取到contentView。<br><img src="/Android-Window-Mechanism/window-02.png" alt="&quot;SetContentView加载流程&quot;"></p><h1 id="Window类型"><a href="#Window类型" class="headerlink" title="Window类型"></a>Window类型</h1><p>添加窗口是通过WindowManagerGlobal的addView方法操作的，这里有三个必要参数。view，params，display。<br>display : 表示要输出的显示设备。<br>view : 表示要显示的View，一般是对该view的上下文进行操作。(view.getContext())<br>params : 类型为WindowManager.LayoutParams，即表示该View要展示在窗口上的布局参数。其中有一个重要的参数type，用来表示窗口的类型。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">WindowManagerGlobal.java</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Provides low-level communication with the system window manager for</span></span><br><span class="line"><span class="comment"> * operations that are not associated with any particular context.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This class is only used internally to implement global functions where</span></span><br><span class="line"><span class="comment"> * the caller already knows the display and relevant compatibility information</span></span><br><span class="line"><span class="comment"> * for the operation.  For most purposes, you should use &#123;<span class="doctag">@link</span> WindowManager&#125; instead</span></span><br><span class="line"><span class="comment"> * since it is bound to a context.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> WindowManagerImpl</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowManagerGlobal</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"WindowManager"</span>;</span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addView</span><span class="params">(View view, ViewGroup.LayoutParams params,</span></span></span><br><span class="line"><span class="function"><span class="params">            Display display, Window parentWindow)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (view == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"view must not be null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (display == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"display must not be null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!(params <span class="keyword">instanceof</span> WindowManager.LayoutParams)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Params must be WindowManager.LayoutParams"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br></pre></td></tr></table></figure></p><p>打开WindowManager类，看到静态内部类。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">WindowManager.java</span><br><span class="line"><span class="meta">@SystemService</span>(Context.WINDOW_SERVICE)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">WindowManager</span> <span class="keyword">extends</span> <span class="title">ViewManager</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span></span><br><span class="line">    <span class="keyword">int</span> DOCKED_INVALID = -<span class="number">1</span>;</span><br><span class="line">    .......</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">LayoutParams</span> <span class="keyword">extends</span> <span class="title">ViewGroup</span>.<span class="title">LayoutParams</span> <span class="keyword">implements</span> <span class="title">Parcelable</span> </span>&#123;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">int</span> type;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">int</span> flags;</span><br><span class="line">    ......</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 可以看到在LayoutParams中，有2个比较重要的参数: flags,type。<br> 我们简要的分析一下flags,该参数表示Window的属性，它有很多选项，通过这些选项可以控制Window的显示特性，这里主要介绍几个比较常用的选项。<br> • FLAG_NOT_FOCUSABLE<br> 表示Window不需要获取焦点，也不需要接收各种输入事件，此标记会同时启用FLAG_NOT_TOUCH_MODAL，最终事件会直接传递给下层具有焦点的Window。</p><p> • FLAG_NOT_TOUCH_MODAL<br> 系统会将当前Window区域以外的单击事件传递给底层的Window，当前Window区域以内的单击事件则自己处理，这个标记很重要，一般来说都需要开启此标记，否则其他Window将无法接收到单击事件。</p><p> • FLAG_SHOW_WHEN_LOCKED<br> 开启此模式可以让Window显示在锁屏的界面上。</p><blockquote><p>Type参数表示Window的类型，Window有三种类型，分别是应用Window、子Window、系统Window。应用类Window对应着一个Activity。子Window不能单独存在，它需要附属在特定的父Window之中，比如常见的PopupWindow就是一个子Window。有些系统Window是需要声明权限才能创建的Window，比如Toast和系统状态栏这些都是系统Window。</p></blockquote><h2 id="应用窗口"><a href="#应用窗口" class="headerlink" title="应用窗口"></a>应用窗口</h2><p>Activity 对应的窗口类型是应用窗口， 所有 Activity 默认的窗口类型是 TYPE_BASE_APPLICATION。<br>WindowManager 的 LayoutParams 的默认类型是 TYPE_APPLICATION。 Dialog 并没有设置type，所以也是默认的窗口类型即 TYPE_APPLICATION。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManager  LayoutParams的默认构造方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LayoutParams</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);</span><br><span class="line">    type = TYPE_APPLICATION;</span><br><span class="line">    format = PixelFormat.OPAQUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><table><thead><tr><th>type</th><th style="text-align:center">层级    类型</th></tr></thead><tbody><tr><td>FIRST_APPLICATION_WINDOW=1</td><td style="text-align:center">开始应用程序窗口，第一个普通应用窗口</td></tr><tr><td>TYPE_BASE_APPLICATION=1</td><td style="text-align:center">所有程序窗口的base窗口，其他应用程序窗口都显示在它上面</td></tr><tr><td>TYPE_APPLICATION=2</td><td style="text-align:center">普通应用程序窗口，token必须设置为Activity的token来指定窗口属于谁</td></tr><tr><td>TYPE_APPLICATION_STARTING=3</td><td style="text-align:center">应用程序启动时先显示此窗口，当真正的窗口配置完成后，关闭此窗口</td></tr><tr><td>LAST_APPLICATION_WINDOW=99</td><td style="text-align:center">最后一个应用窗口</td></tr></tbody></table><h2 id="子窗口"><a href="#子窗口" class="headerlink" title="子窗口"></a>子窗口</h2><p> 子窗口不能单独存在，它需要附属在特定的父Window之中，例如开篇第一张图，绿色框框即为popupWindow，它就是子窗口，类型一般为TYPE_APPLICATION_PANEL。之所以称为子窗口，即它的父窗口显示时，子窗口才显示。父窗口不显示，它也不显示。追随父窗口。<br> <a href="https://blog.csdn.net/ekeuy/article/details/42398009" target="_blank" rel="noopener">那么问题又来了，我们能否在Acitivty的onCreate()中创建popupWindow并显示呢？</a></p><table><thead><tr><th>type</th><th style="text-align:center">层级    类型</th></tr></thead><tbody><tr><td>FIRST_SUB_WINDOW=1000</td><td style="text-align:center">第一个子窗口</td></tr><tr><td>TYPE_APPLICATION_PANEL=1000</td><td style="text-align:center">应用窗口的子窗口,popupWindow的默认类型</td></tr><tr><td>TYPE_APPLICATION_MEDIA=1001</td><td style="text-align:center">媒体窗口</td></tr><tr><td>TYPE_APPLICATION_SUB_PANEL=1002</td><td style="text-align:center">TYPE_APPLICATION_PANE的子窗口</td></tr><tr><td>TYPE_APPLICATION_ATTACHED_DIALOG=1003</td><td style="text-align:center">对话框，类似于面板窗口(OptionMenu,ContextMenu)</td></tr><tr><td>TYPE_APPLICATION_MEDIA_OVERLAY=1004</td><td style="text-align:center">媒体信息，显示在媒体层和程序窗口之间，需要实现半透明效果</td></tr><tr><td>LAST_SUB_WINDOW=1999</td><td style="text-align:center">最后一个子窗口</td></tr></tbody></table><h2 id="系统窗口"><a href="#系统窗口" class="headerlink" title="系统窗口"></a>系统窗口</h2><p> 系统窗口跟应用窗口不同，不需要对应 Activity。跟子窗口不同，不需要有父窗口。一般来讲，系统窗口应该由系统来创建的，例如发生异常，ANR时的提示框，又如系统状态栏，屏保等。但是，Framework 还是定义了一些，可以被应用所创建的系统窗口。</p><table><thead><tr><th>type</th><th style="text-align:center">层级    类型</th></tr></thead><tbody><tr><td>FIRST_SYSTEM_WINDOW=2000</td><td style="text-align:center">第一个系统窗口</td></tr><tr><td>TYPE_STATUS_BAR=2000</td><td style="text-align:center">状态栏，只能有一个状态栏，位于屏幕顶端</td></tr><tr><td>TYPE_SEARCH_BAR =2001</td><td style="text-align:center">搜索栏</td></tr><tr><td>TYPE_PHONE=2002</td><td style="text-align:center">电话窗口，它用于电话交互</td></tr><tr><td>TYPE_SYSTEM_ALERT=2003</td><td style="text-align:center">系统警告，出现在应用程序窗口之上</td></tr><tr><td>TYPE_KEYGUARD=2004</td><td style="text-align:center">锁屏窗口</td></tr><tr><td>TYPE_TOAST=2005</td><td style="text-align:center">信息窗口，用于显示Toast</td></tr><tr><td>TYPE_SYSTEM_OVERLAY=2006</td><td style="text-align:center">系统顶层窗口，显示在其他内容之上，此窗口不能获得输入焦点，否则影响锁屏</td></tr><tr><td>TYPE_PRIORITY_PHONE=2007</td><td style="text-align:center">当锁屏时显示的来电显示窗口</td></tr><tr><td>TYPE_SYSTEM_DIALOG=2008</td><td style="text-align:center">系统对话框</td></tr><tr><td>TYPE_KEYGUARD_DIALOG=2009</td><td style="text-align:center">锁屏时显示的对话框</td></tr><tr><td>TYPE_SYSTEM_ERROR=2010</td><td style="text-align:center">系统内部错误提示</td></tr><tr><td>TYPE_INPUT_METHOD=2011</td><td style="text-align:center">输入法窗口，显示于普通应用/子窗口之上</td></tr><tr><td>TYPE_INPUT_METHOD_DIALOG=2012</td><td style="text-align:center">输入法中备选框对应的窗口</td></tr><tr><td>TYPE_WALLPAPER=2013</td><td style="text-align:center">墙纸窗口</td></tr><tr><td>TYPE_STATUS_BAR_PANEL=2014</td><td style="text-align:center">滑动状态条后出现的窗口</td></tr><tr><td>TYPE_SECURE_SYSTEM_OVERLAY=2015</td><td style="text-align:center">安全系统覆盖窗口</td></tr><tr><td>……</td><td style="text-align:center">……</td></tr><tr><td>LAST_SYSTEM_WINDOW=2999</td><td style="text-align:center">最后一个系统窗口</td></tr></tbody></table><p> 那么，这个type层级到底有什么作用呢？<br> Window是分层的，每个Window都有对应的z-ordered，（z轴，从1层层叠加到2999，你可以将屏幕想成三维坐标模式）层级大的会覆盖在层级小的Window上面。</p><blockquote><p>在三类Window中，应用Window的层级范围是1~99。子Window的层级范围是1000~1999，系统Window的层级范围是2000~2999，这些层级范围对应着WindowManager.LayoutParams的type参数。如果想要Window位于所有Window的最顶层，那么采用较大的层级即可。另外有些系统层级的使用是需要声明权限的。</p></blockquote><h1 id="Window的内部机制-Activity"><a href="#Window的内部机制-Activity" class="headerlink" title="Window的内部机制(Activity)"></a><span id="jump">Window的内部机制(Activity)</span></h1><p> Window的类关系图如下：<br> <img src="/Android-Window-Mechanism/window-03.png" alt="&quot;Window的类关系图&quot;"></p><blockquote><p>Window是一个抽象的概念，每一个Window都对应着一个View和一个ViewRootImpl，Window和View通过ViewRootImpl来建立联系，因此Window并不是不存在的，它是以View的形式存在。这点从ViewManager的定义也可以看得出来，它只提供三个接口方法：addView、updateViewLayout、removeView，这些方法都是针对View的。这说明View才是Window存在的实体。</p></blockquote><h2 id="Window的创建过程"><a href="#Window的创建过程" class="headerlink" title="Window的创建过程"></a>Window的创建过程</h2><p> 首先要分析Window的创建过程，就必须了解<a href="../Android-Launcher-Activity">Activity的启动过程</a>。<br> Activity的启动过程很复杂，最终会由ActivityThread中的handleLaunchActivity()来完成整个启动过程。<br> 在这个方法中会通过performLaunchActivity()方法创建Activity，performLaunchActivity()内部通过类加载器创建Activity的实例对象，并调用其attach()方法为其关联运行过程中所依赖的一系列上下文环境变量以及创建与绑定窗口。<br> 具体不在赘述，更多Activity启动细节请移步<a href="../Android-Launcher-Activity">Activity的启动过程</a>。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread.java</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleLaunchActivity</span><span class="params">(ActivityClientRecord r, Intent customIntent)</span> </span>&#123;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//获取WindowManagerService的Binder引用(proxy端)。</span></span><br><span class="line">    WindowManagerGlobal.initialize();</span><br><span class="line">    <span class="comment">//会调用Activity的onCreate,onStart,onResotreInstanceState方法</span></span><br><span class="line">    Activity a = performLaunchActivity(r, customIntent);</span><br><span class="line">    <span class="keyword">if</span> (a != <span class="keyword">null</span>) &#123;</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//会调用Activity的onResume方法.</span></span><br><span class="line">        handleResumeActivity(r.token, <span class="keyword">false</span>, r.isForward,</span><br><span class="line">                !r.activity.mFinished &amp;&amp; !r.startsNotResumed, r.lastProcessedSeq, reason);</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Activity <span class="title">performLaunchActivity</span><span class="params">(ActivityClientRecord r, Intent customIntent)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//通过类加载器创建Activity</span></span><br><span class="line">        Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//通过LoadedApk的makeApplication方法来创建Application对象</span></span><br><span class="line">        Application app = r.packageInfo.makeApplication(<span class="keyword">false</span>, mInstrumentation);</span><br><span class="line">        <span class="keyword">if</span> (activity != <span class="keyword">null</span>) &#123;</span><br><span class="line">            ......</span><br><span class="line">            activity.attach(appContext, <span class="keyword">this</span>, getInstrumentation(), r.token,</span><br><span class="line">                    r.ident, app, r.intent, r.activityInfo, title, r.parent,</span><br><span class="line">                    r.embeddedID, r.lastNonConfigurationInstances, config,</span><br><span class="line">                    r.referrer, r.voiceInteractor, window);</span><br><span class="line">            ......</span><br><span class="line">            <span class="comment">//onCreate</span></span><br><span class="line">            mInstrumentation.callActivityOnCreate(activity, r.state);</span><br><span class="line">            <span class="comment">//onStart</span></span><br><span class="line">            activity.performStart();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> activity;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 在Activity的attach()方法里，系统会创建Activity所属的Window对象并为其设置回调接口，由于Activity实现了Window的Callback接口，因此当Window接收到外界的状态改变时就会回调Activity的方法。Callback接口中的方法很多，下面举几个比较眼熟的方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Callback</span> </span>&#123;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">dispatchTouchEvent</span><span class="params">(MotionEvent event)</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> View <span class="title">onCreatePanelView</span><span class="params">(<span class="keyword">int</span> featureId)</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onMenuItemSelected</span><span class="params">(<span class="keyword">int</span> featureId, MenuItem item)</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onContentChanged</span><span class="params">()</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onWindowFocusChanged</span><span class="params">(<span class="keyword">boolean</span> hasFocus)</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAttachedToWindow</span><span class="params">()</span></span>;</span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDetachedFromWindow</span><span class="params">()</span></span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Activity.java</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">attach</span><span class="params">(Context context, ActivityThread aThread,</span></span></span><br><span class="line"><span class="function"><span class="params">            Instrumentation instr, IBinder token, <span class="keyword">int</span> ident,</span></span></span><br><span class="line"><span class="function"><span class="params">            Application application, Intent intent, ActivityInfo info,</span></span></span><br><span class="line"><span class="function"><span class="params">            CharSequence title, Activity parent, String id,</span></span></span><br><span class="line"><span class="function"><span class="params">            NonConfigurationInstances lastNonConfigurationInstances,</span></span></span><br><span class="line"><span class="function"><span class="params">            Configuration config, String referrer, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params">            Window window, ActivityConfigCallback activityConfigCallback)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//绑定上下文</span></span><br><span class="line">        attachBaseContext(context);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建Window,PhoneWindow是Window的唯一具体实现类</span></span><br><span class="line">        mWindow = <span class="keyword">new</span> PhoneWindow(<span class="keyword">this</span>, window);<span class="comment">//此处的window==null，但不影响</span></span><br><span class="line">        mWindow.setWindowControllerCallback(<span class="keyword">this</span>);</span><br><span class="line">        mWindow.setCallback(<span class="keyword">this</span>);</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//设置WindowManager</span></span><br><span class="line">        mWindow.setWindowManager(</span><br><span class="line">                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),</span><br><span class="line">                mToken, mComponent.flattenToString(),</span><br><span class="line">                (info.flags &amp; ActivityInfo.FLAG_HARDWARE_ACCELERATED) != <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (mParent != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mWindow.setContainer(mParent.getWindow());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//创建完后通过getWindowManager就可以得到WindowManager实例</span></span><br><span class="line">        mWindowManager = mWindow.getWindowManager();<span class="comment">//其实它是WindowManagerImpl</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getSystemService</span><span class="params">(@ServiceName @NonNull String name)</span> </span>&#123;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">if</span> (WINDOW_SERVICE.equals(name)) &#123;</span><br><span class="line">            <span class="keyword">return</span> mWindowManager;</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.getSystemService(name);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p> 等，等一下。看到这里是不是有点懵，Activity的getSystemService根本没有创建WindowManager，那么mWindow.setWindowManager()设置的岂不是空的WindowManager，那这样它的下一步mWindowManager = mWindow.getWindowManager()岂不是无线空循环？<br> mWindowManager是由mWindow.getWindowManager()赋值，我们到window里面看看<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Window.java</span></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Return the window manager allowing this Window to display its own</span></span><br><span class="line"><span class="comment">     * windows.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> WindowManager The ViewManager.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> WindowManager <span class="title">getWindowManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mWindowManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Set the window manager for use by this Window to, for example,</span></span><br><span class="line"><span class="comment">     * display panels.  This is &lt;em&gt;not&lt;/em&gt; used for displaying the</span></span><br><span class="line"><span class="comment">     * Window itself -- that must be done by the client.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> wm The window manager for adding new windows.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWindowManager</span><span class="params">(WindowManager wm, IBinder appToken, String appName,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> hardwareAccelerated)</span> </span>&#123;</span><br><span class="line">        mAppToken = appToken;</span><br><span class="line">        mAppName = appName;</span><br><span class="line">        mHardwareAccelerated = hardwareAccelerated</span><br><span class="line">                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, <span class="keyword">false</span>);</span><br><span class="line">        <span class="keyword">if</span> (wm == <span class="keyword">null</span>) &#123;</span><br><span class="line">            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">////在此处创建mWindowManager </span></span><br><span class="line">        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 我们看到在Window类中，setWindowManger里面获取到mWindowManager,接着往下看，到WindowManagerImpl.java中<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerImpl.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowManagerImpl</span> <span class="keyword">implements</span> <span class="title">WindowManager</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Context mContext;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Window mParentWindow;</span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">public</span> WindowManagerImpl <span class="title">createLocalWindowManager</span><span class="params">(Window parentWindow)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> WindowManagerImpl(mContext, parentWindow);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 类似于PhoneWindow和Window的关系，WindowManager是一个接口，具体的实现是WindowManagerImpl。</p><p> 到了这里Window已经创建完毕，在上面的performLaunchActivity()方法中我们可以看到调用了onCreate()方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread --&gt; performLaunchActivity</span></span><br><span class="line">            mInstrumentation.callActivityOnCreate(activity, r.state);</span><br></pre></td></tr></table></figure></p><p> 在Activity的onCreate方法里，我们通过setContentView()将view添加到DecorView的mContentParent中，也就是将资源布局文件和phoneWindow关联。<br> 所以在PhoneWindow的最后，因为Activity实现了Callback接口，便可以通过下面代码通知Activity视图已经发生改变。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//PhoneWindow  --&gt;  setContentView</span></span><br><span class="line">    callback.onContentChanged();</span><br></pre></td></tr></table></figure></p><blockquote><p>经过了上面几个过程，Window和DecorView已经被创建并初始化完毕，Activity的布局文件也成功添加到了DecorView的mContentParent中，但这个时候的DecorView还没有被WindowManager正式添加到Window中。</p></blockquote><blockquote><p>这里需要理解的是，Window更多表示的是一种抽象功能集合，虽然说早在Activity的attach方法中window就已经被创建了，但是这个时候由于DecorView并没有被WindowManager识别，所以这个时候的Window暂时无法提供具体功能。</p></blockquote><blockquote><p>总的来说，Window可以成功使用有2个标志:<br>①View绘制完毕，可以呈现给用户。<br>②View可以接收外界信息（触摸事件等）。</p></blockquote><h2 id="Window的添加过程"><a href="#Window的添加过程" class="headerlink" title="Window的添加过程"></a>Window的添加过程</h2><p> PhoneWindow 只是负责处理一些应用窗口通用的逻辑(设置标题栏，导航栏等)。但是真正完成把一个 View，作为窗口添加到 WmS 的过程是由 WindowManager 来完成的。WindowManager 的具体实现是 WindowManagerImpl。</p><p> 下面我们继续来分析handleLaunchActivity()方法中handleResumeActivity()的执行过程。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread.java</span></span><br><span class="line"> <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">handleResumeActivity</span><span class="params">(IBinder token,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> clearHide, <span class="keyword">boolean</span> isForward, <span class="keyword">boolean</span> reallyResume, <span class="keyword">int</span> seq, String reason)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//把activity数据记录更新到ActivityClientRecord</span></span><br><span class="line">        ActivityClientRecord r = mActivities.get(token);</span><br><span class="line">        r = performResumeActivity(token, clearHide, reason);</span><br><span class="line">        <span class="keyword">if</span> (r != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (r.window == <span class="keyword">null</span> &amp;&amp; !a.mFinished &amp;&amp; willBeVisible) &#123;</span><br><span class="line">                r.window = r.activity.getWindow();</span><br><span class="line">                View decor = r.window.getDecorView();</span><br><span class="line">                decor.setVisibility(View.INVISIBLE);<span class="comment">//不可见</span></span><br><span class="line">                ViewManager wm = a.getWindowManager();</span><br><span class="line">                WindowManager.LayoutParams l = r.window.getAttributes();</span><br><span class="line">                a.mDecor = decor;</span><br><span class="line">                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;</span><br><span class="line">                ......</span><br><span class="line">                <span class="keyword">if</span> (a.mVisibleFromClient &amp;&amp; !a.mWindowAdded) &#123;</span><br><span class="line">                    a.mWindowAdded = <span class="keyword">true</span>;</span><br><span class="line">                    wm.addView(decor, l);<span class="comment">//把decor添加到窗口上（划重点）</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125; </span><br><span class="line">                <span class="comment">//屏幕参数发生了改变</span></span><br><span class="line">                performConfigurationChanged(r.activity, r.tmpConfig);</span><br><span class="line">                WindowManager.LayoutParams l = r.window.getAttributes();</span><br><span class="line">                    <span class="keyword">if</span> (r.activity.mVisibleFromClient) &#123;</span><br><span class="line">                        ViewManager wm = a.getWindowManager();</span><br><span class="line">                        View decor = r.window.getDecorView();</span><br><span class="line">                        wm.updateViewLayout(decor, l);<span class="comment">//更新窗口状态</span></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                ......</span><br><span class="line">                <span class="keyword">if</span> (r.activity.mVisibleFromClient) &#123;</span><br><span class="line">                    <span class="comment">//已经成功添加到窗口上了（绘制和事件接收），设置为可见</span></span><br><span class="line">                    r.activity.makeVisible();</span><br><span class="line">                &#125;</span><br><span class="line">            <span class="comment">//通知ActivityManagerService，Activity完成Resumed</span></span><br><span class="line">             ActivityManagerNative.getDefault().activityResumed(token);</span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 在上面代码中，首先配置ActivityClientRecord，之后将DecorView设置为INVISIBLE，因为View并未绘制完成，当前的DecorView只是一个有结构的空壳。<br> 然后通过WindowManagerImpl将DecorView正式的添加到窗口上wm.addView(decor, l);，这一步非常非常重要，因为它包括了2个比较重要和常见的过程：<strong>Window的添加过程</strong>和<strong>View的绘制流程</strong>。</p><p> 下面画一张图来看看整体添加流程，留一个大致的概括印象。<br> <img src="/Android-Window-Mechanism/window-04.png" alt="&quot;WindowManager中addView的流程&quot;"></p><blockquote><p>窗口的添加过程如上图所示，我们知道 WmS 运行在单独的进程中。这里 IWindowSession 执行的 addtoDisplay 操作应该是 IPC 调用。接下来的Window添加过程，我们会知道<strong>每个应用窗口创建时，最终都会创建一个 ViewRootImpl 对象</strong>。</p></blockquote><blockquote><p>ViewRootImpl 是一很重要的类，类似 ApplicationThread 负责跟AmS通信一样，<strong>ViewRootImpl 的一个重要职责就是跟 WmS 通信，它通静态变量 sWindowSession（IWindowSession实例）与 WmS 进行通信</strong>。</p></blockquote><blockquote><p>每个应用进程，仅有一个 sWindowSession 对象，它对应了 WmS 中的 Session 子类，WmS 为每一个应用进程分配一个 Session 对象。WindowState 类有一个 IWindow mClient 参数，是由 Session 调用 addToDisplay 传递过来的，对应了 ViewRootImpl 中的 W 类的实例。</p></blockquote><blockquote><p>简单的总结一下，ViewRootImpl通过IWindowSession远程IPC通知WmS，并且由W类接收WmS的远程IPC通知。（这个W类和ActivityThread的H类同样精悍的命名，并且也是同样的工作职责！）</p></blockquote><p> 图解完Window的添加过程，对整个流程有一个印象和思路，那么下面继续分析源码。<br> 在上面的handleResumeActivity()方法中，我们看到源码通过wm.addView(decor, l);操作DecorView和WindowManager.LayoutParams。上面讲解也说过，因为WindowManager是接口，真正具体实现类是windowManagerImpl。如果Window中类的关系还不太清楚的可以再回到上面的<a href="#jump">「Window的内部机制」</a>看图解。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerImpl.java</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addView</span><span class="params">(@NonNull View view, @NonNull ViewGroup.LayoutParams params)</span> </span>&#123;</span><br><span class="line">        applyDefaultToken(params);</span><br><span class="line">        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateViewLayout</span><span class="params">(@NonNull View view, @NonNull ViewGroup.LayoutParams params)</span> </span>&#123;</span><br><span class="line">        applyDefaultToken(params);</span><br><span class="line">        mGlobal.updateViewLayout(view, params);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">removeView</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        mGlobal.removeView(view, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p> 我们看到这个WindowManagerImpl原来也是一个吃空饷的家伙！对于Window(或者可以说是View)的操作都是交由WindowManagerGlobal来处理，WindowManagerGlobal以工厂的形式向外提供自己的实例。这种工作模式是桥接模式，将所有的操作全部委托给WindowManagerGlobal来实现。</p><p> 在WindowManagerImpl的全局变量中通过单例模式初始化了WindowManagerGlobal，也就是说<strong>一个进程就只有一个WindowManagerGlobal对象</strong>。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowManagerImpl</span> <span class="keyword">implements</span> <span class="title">WindowManager</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Context mContext;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Window mParentWindow;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerGlobal.java</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Provides low-level communication with the system window manager for</span></span><br><span class="line"><span class="comment"> * operations that are not associated with any particular context.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This class is only used internally to implement global functions where</span></span><br><span class="line"><span class="comment"> * the caller already knows the display and relevant compatibility information</span></span><br><span class="line"><span class="comment"> * for the operation.  For most purposes, you should use &#123;<span class="doctag">@link</span> WindowManager&#125; instead</span></span><br><span class="line"><span class="comment"> * since it is bound to a context.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> WindowManagerImpl</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowManagerGlobal</span> </span>&#123;</span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"WindowManager"</span>;</span><br><span class="line">   ...</span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addView</span><span class="params">(View view, ViewGroup.LayoutParams params,</span></span></span><br><span class="line"><span class="function"><span class="params">            Display display, Window parentWindow)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (view == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"view must not be null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (display == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"display must not be null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!(params <span class="keyword">instanceof</span> WindowManager.LayoutParams)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Params must be WindowManager.LayoutParams"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">final</span> WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;</span><br><span class="line">        <span class="keyword">if</span> (parentWindow != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//调整布局参数，并设置token</span></span><br><span class="line">            parentWindow.adjustLayoutParamsForSubWindow(wparams);</span><br><span class="line">        &#125; </span><br><span class="line">        ViewRootImpl root;</span><br><span class="line">        View panelParentView = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">            <span class="keyword">int</span> index = findViewLocked(view, <span class="keyword">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (mDyingViews.contains(view)) &#123;</span><br><span class="line">                    <span class="comment">//如果待删除的view中有当前view，删除它</span></span><br><span class="line">                    <span class="comment">// Don't wait for MSG_DIE to make it's way through root's queue.</span></span><br><span class="line">                    mRoots.get(index).doDie();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// The previous removeView() had not completed executing. Now it has.</span></span><br><span class="line">                <span class="comment">//之前移除View并没有完成删除操作，现在正式删除该view</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//如果这是一个子窗口个(popupWindow)，找到它的父窗口。</span></span><br><span class="line">            <span class="comment">//最本质的作用是使用父窗口的token(viewRootImpl的W类，也就是IWindow)</span></span><br><span class="line">            <span class="keyword">if</span> (wparams.type &gt;= WindowManager.LayoutParams.FIRST_SUB_WINDOW &amp;&amp;</span><br><span class="line">                    wparams.type &lt;= WindowManager.LayoutParams.LAST_SUB_WINDOW) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> count = mViews.size();</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mRoots.get(i).mWindow.asBinder() == wparams.token) &#123;</span><br><span class="line">                        panelParentView = mViews.get(i);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//创建ViewRootImpl，并且将view与之绑定</span></span><br><span class="line">            root = <span class="keyword">new</span> ViewRootImpl(view.getContext(), display);</span><br><span class="line">            view.setLayoutParams(wparams);</span><br><span class="line">            mViews.add(view);<span class="comment">//将当前view添加到mViews集合中</span></span><br><span class="line">            mRoots.add(root);<span class="comment">//将当前ViewRootImpl添加到mRoots集合中</span></span><br><span class="line">            mParams.add(wparams);<span class="comment">//将当前window的params添加到mParams集合中</span></span><br><span class="line">        &#125;</span><br><span class="line">          ......</span><br><span class="line">            <span class="comment">//通过ViewRootImpl的setView方法，完成view的绘制流程，并添加到window上。</span></span><br><span class="line">            root.setView(view, wparams, panelParentView);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p> 在上面代码中有2个比较重要的知识点:<br> 1、在WindowManagerGlobal中有如下几个重要的集合<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//存储所有Window对应的View</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;View&gt; mViews = <span class="keyword">new</span> ArrayList&lt;View&gt;();</span><br><span class="line"><span class="comment">//存储所有Window对应的ViewRootImpl</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;ViewRootImpl&gt; mRoots = <span class="keyword">new</span> ArrayList&lt;ViewRootImpl&gt;();</span><br><span class="line"><span class="comment">//存储所有Window对应的布局参数</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;WindowManager.LayoutParams&gt; mParams =</span><br><span class="line">        <span class="keyword">new</span> ArrayList&lt;WindowManager.LayoutParams&gt;();</span><br><span class="line"><span class="comment">//存储正被删除的View对象（已经调用removeView但是还未完成删除操作的Window对象）     </span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ArraySet&lt;View&gt; mDyingViews = <span class="keyword">new</span> ArraySet&lt;View&gt;();</span><br></pre></td></tr></table></figure></p><p> 2、token<br> 在源码中token一般代表的是Binder对象，作用于IPC进程间数据通讯。并且它也包含着此次通讯所需要的信息，在ViewRootImpl里，token用来表示mWindow(W类，即IWindow)，并且在WmS中只有符合要求的token才能让Window正常显示。所以上面提出的问题<br> 我们能否在Acitivty的onCreate()中创建popupWindow并显示呢？<br> 就与之有关，我们暂时把token放一边，走完Window的添加过程先。(最后分析token)</p><p> 言归正传，我们继续看代码。<br> 在WindowManagerGlobal的addView()方法里，最后调用ViewRootImpl的setView方法，处理添加过程。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerGlobal  --&gt;  addView</span></span><br><span class="line">            <span class="comment">//创建ViewRootImpl，并且将view与之绑定</span></span><br><span class="line">            root = <span class="keyword">new</span> ViewRootImpl(view.getContext(), display);</span><br><span class="line">            <span class="comment">//通过ViewRootImpl的setView方法，完成view的绘制流程，并添加到window上。</span></span><br><span class="line">            root.setView(view, wparams, panelParentView);</span><br></pre></td></tr></table></figure></p><p> 通过上面这个代码可知，WindowManagerGlobal将View的处理操作全权交给ViewRootImpl，而且上面我们也提到了，View成功添加到Window，无非就是展现视图和用户交互。</p><blockquote><p>①<strong>View绘制完毕，可以呈现给用户</strong>。<br>②<strong>View可以接收外界信息（触摸事件等）</strong>。<br> 在ViewRootImpl的setView()方法中，将会完成上面2个艰巨而又伟大的任务。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ViewRootImpl.java</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setView</span><span class="params">(View view, WindowManager.LayoutParams attrs, View panelParentView)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">int</span> res; </span><br><span class="line">                <span class="comment">// Schedule the first layout -before- adding to the window</span></span><br><span class="line">                <span class="comment">// manager, to make sure we do the relayout before receiving</span></span><br><span class="line">                <span class="comment">// any other events from the system.</span></span><br><span class="line">                requestLayout();<span class="comment">//View的绘制流程</span></span><br><span class="line">                <span class="keyword">if</span> ((mWindowAttributes.inputFeatures</span><br><span class="line">                        &amp; WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="comment">//创建InputChannel</span></span><br><span class="line">                    mInputChannel = <span class="keyword">new</span> InputChannel();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//通过WindowSession进行IPC调用，将View添加到Window上</span></span><br><span class="line">                    <span class="comment">//mWindow即W类，用来接收WmS信息</span></span><br><span class="line">                    <span class="comment">//同时通过InputChannel接收触摸事件回调</span></span><br><span class="line">                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,</span><br><span class="line">                            getHostVisibility(), mDisplay.getDisplayId(),</span><br><span class="line">                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,</span><br><span class="line">                            mAttachInfo.mOutsets, mInputChannel);</span><br><span class="line">                &#125;</span><br><span class="line">                ......</span><br><span class="line">                    <span class="comment">//处理触摸事件回调</span></span><br><span class="line">                    mInputEventReceiver = <span class="keyword">new</span> WindowInputEventReceiver(mInputChannel,</span><br><span class="line">                            Looper.myLooper());</span><br><span class="line">                ......</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p></blockquote><p> 在ViewRootImpl的setView()方法里，执行requestLayout()方法完成[<strong>View的绘制流程</strong>]（），并且通过WindowSession将View和InputChannel添加到WmS中，从而将View添加到Window上并且接收触摸事件。</p><p> 通过mWindowSession来完成Window的添加过程 ，mWindowSession的类型是IWindowSession，是一个Bindler对象，真正的实现类是Session，也就是Window的添加是一次IPC调用。(mWindowSession在ViewRootImpl的构造函数中通过WindowManagerGlobal.getWindowSession();创建)</p><p> 同时将mWindow（即 W extends IWindow.Stub）发送给WmS，用来接收WmS信息。<br> <img src="/Android-Window-Mechanism/window-05.png" alt="&quot;ViewRoot和WMS通信&quot;"><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Session</span></span><br><span class="line">    <span class="keyword">final</span> WindowManagerService mService;</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">addToDisplay</span><span class="params">(IWindow window, <span class="keyword">int</span> seq, WindowManager.LayoutParams attrs,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> viewVisibility, <span class="keyword">int</span> displayId, Rect outContentInsets, Rect outStableInsets,</span></span></span><br><span class="line"><span class="function"><span class="params">            Rect outOutsets, InputChannel outInputChannel)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> mService.addWindow(<span class="keyword">this</span>, window, seq, attrs, viewVisibility, displayId,</span><br><span class="line">                outContentInsets, outStableInsets, outOutsets, outInputChannel);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 如此一来，Window的添加请求就交给WmS去处理了，在WmS内部会为每一个应用保留一个单独的Session。在WmS 端会创建一个WindowState对象用来表示当前添加的窗口。 WmS负责管理这里些 WindowState 对象。至此，Window的添加过程就结束了。</p><p> 至于Window的删除和更新过程，举一反三，也是使用WindowManagerGlobal对ViewRootImpl的操作，最终也是通过Session的IPC跨进程通信通知到WmS。整个过程的本质都是同出一辙的。<br> <img src="/Android-Window-Mechanism/window-06.png" alt="&quot;WMS对View的管理&quot;"></p><h1 id="Window的token"><a href="#Window的token" class="headerlink" title="Window的token"></a>Window的token</h1><p> 在WindowManager的LayoutParams中，与type同等重要的还有token。</p><p> 上面说到：在源码中token一般代表的是Binder对象，作用于IPC进程间数据通讯。并且它也包含着此次通讯所需要的信息，在ViewRootImpl里，token用来表示mWindow(W类，即IWindow)，并且在WmS中只有符合要求的token才能让Window正常显示。</p><p> 先剧透一下，在Window中，token(LayoutParams中的token)分为以下2种情况：<br> • 应用窗口 : token表示的是activity的mToken(ActivityRecord)<br> • 子窗口 : token表示的是父窗口的W对象，也就是mWindow(IWindow)</p><h2 id="Activity的attach"><a href="#Activity的attach" class="headerlink" title="Activity的attach()"></a>Activity的attach()</h2><p> 我们通过源码分析token的流程，顺带再巩固一遍Window的创建流程。<br> 在Activity的attach()方法中：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Activity.java</span></span><br><span class="line"> <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">attach</span><span class="params">(Context context, ActivityThread aThread,</span></span></span><br><span class="line"><span class="function"><span class="params">            Instrumentation instr, IBinder token, <span class="keyword">int</span> ident,</span></span></span><br><span class="line"><span class="function"><span class="params">            Application application, Intent intent, ActivityInfo info,</span></span></span><br><span class="line"><span class="function"><span class="params">            CharSequence title, Activity parent, String id,</span></span></span><br><span class="line"><span class="function"><span class="params">            NonConfigurationInstances lastNonConfigurationInstances,</span></span></span><br><span class="line"><span class="function"><span class="params">            Configuration config, String referrer, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params">            Window window)</span> </span>&#123;</span><br><span class="line">        attachBaseContext(context);</span><br><span class="line">        mToken = token;<span class="comment">//ActivityRecord</span></span><br><span class="line">        mWindow = <span class="keyword">new</span> PhoneWindow(<span class="keyword">this</span>, window);</span><br><span class="line">        ......</span><br><span class="line">        mWindow.setWindowManager(</span><br><span class="line">                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),</span><br><span class="line">                mToken, mComponent.flattenToString(),</span><br><span class="line">                (info.flags &amp; ActivityInfo.FLAG_HARDWARE_ACCELERATED) != <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (mParent != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mWindow.setContainer(mParent.getWindow());</span><br><span class="line">        &#125;</span><br><span class="line">        mWindowManager = mWindow.getWindowManager();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><h2 id="创建windowManager"><a href="#创建windowManager" class="headerlink" title="创建windowManager"></a>创建windowManager</h2><p> 在Window中创建windowManager，并且全局变量mAppToken就是上面代码传入的mToken(ActivityRecord)<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Window.java</span></span><br><span class="line">    <span class="keyword">private</span> IBinder mAppToken;<span class="comment">//Activity的mToken</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWindowManager</span><span class="params">(WindowManager wm, IBinder appToken, String appName,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> hardwareAccelerated)</span> </span>&#123;</span><br><span class="line">        mAppToken = appToken;</span><br><span class="line">        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerImpl.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> WindowManagerImpl <span class="title">createLocalWindowManager</span><span class="params">(Window parentWindow)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//parentWindow就是activity的Window</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> WindowManagerImpl(mContext, parentWindow);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addView</span><span class="params">(@NonNull View view, @NonNull ViewGroup.LayoutParams params)</span> </span>&#123;</span><br><span class="line">        applyDefaultToken(params);</span><br><span class="line">        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p> 我们拿出上面的关系图，目前已经分析了WindowManagerImpl，接下来就是WindowManagerGlobal的addView()方法。<br><img src="/Android-Window-Mechanism/window-04.png" alt="&quot;Window的界面解析&quot;"></p><h2 id="WindowManagerGlobal"><a href="#WindowManagerGlobal" class="headerlink" title="WindowManagerGlobal"></a>WindowManagerGlobal</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerGlobal.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowManagerGlobal</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"WindowManager"</span>;</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addView</span><span class="params">(View view, ViewGroup.LayoutParams params,</span></span></span><br><span class="line"><span class="function"><span class="params">            Display display, Window parentWindow)</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">final</span> WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;</span><br><span class="line">        <span class="comment">//parentWindow就是Acitivty的Window</span></span><br><span class="line">        <span class="keyword">if</span> (parentWindow != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//①调整布局参数，并设置token</span></span><br><span class="line">            parentWindow.adjustLayoutParamsForSubWindow(wparams);</span><br><span class="line">        &#125; </span><br><span class="line">        ViewRootImpl root;</span><br><span class="line">        View panelParentView = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">            <span class="comment">//②如果这是一个子窗口个(popupWindow)，找到它的父窗口。</span></span><br><span class="line">            <span class="comment">//②最本质的作用是使用父窗口的token(viewRootImpl的W类，也就是IWindow)</span></span><br><span class="line">            <span class="keyword">if</span> (wparams.type &gt;= WindowManager.LayoutParams.FIRST_SUB_WINDOW &amp;&amp;</span><br><span class="line">                    wparams.type &lt;= WindowManager.LayoutParams.LAST_SUB_WINDOW) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> count = mViews.size();</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mRoots.get(i).mWindow.asBinder() == wparams.token) &#123;</span><br><span class="line">                        panelParentView = mViews.get(i);</span><br><span class="line">                        <span class="comment">//从mRoots找到相同的W类，再从mViews找到父View</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//③创建ViewRootImpl，并且将view与之绑定</span></span><br><span class="line">            root = <span class="keyword">new</span> ViewRootImpl(view.getContext(), display);</span><br><span class="line">            view.setLayoutParams(wparams);</span><br><span class="line">            mViews.add(view);</span><br><span class="line">            mRoots.add(root);</span><br><span class="line">            mParams.add(wparams);</span><br><span class="line">        &#125;</span><br><span class="line">            <span class="comment">//④通过ViewRootImpl的setView方法，完成view的绘制流程，并添加到window上。</span></span><br><span class="line">            root.setView(view, wparams, panelParentView);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 我们一步一步分析，先看①，parentWindow.adjustLayoutParamsForSubWindow(wparams);<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Window.java</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">adjustLayoutParamsForSubWindow</span><span class="params">(WindowManager.LayoutParams wp)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//如果它是子窗口</span></span><br><span class="line">        <span class="keyword">if</span> (wp.type &gt;= WindowManager.LayoutParams.FIRST_SUB_WINDOW &amp;&amp;</span><br><span class="line">                wp.type &lt;= WindowManager.LayoutParams.LAST_SUB_WINDOW) &#123;</span><br><span class="line">            <span class="keyword">if</span> (wp.token == <span class="keyword">null</span>) &#123;</span><br><span class="line">                View decor = peekDecorView();</span><br><span class="line">                <span class="keyword">if</span> (decor != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    wp.token = decor.getWindowToken();<span class="comment">//分析View的getWindowToken</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//应用窗口（dialog的情况）</span></span><br><span class="line">            <span class="comment">//此时的wp.token即为mAppToken</span></span><br><span class="line">            <span class="keyword">if</span> (wp.token == <span class="keyword">null</span>) &#123;</span><br><span class="line">                wp.token = mContainer == <span class="keyword">null</span> ? mAppToken : mContainer.mAppToken;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 我们看到如果是应用窗口，wp.token==null的情况，就会给他赋值mAppToken，而这个mAppToken就是我们上面在Activity的attach()方法中传入的mToken!</p><p> 我们再分析子窗口的token，接上面的decor.getWindowToken()<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//View.java</span></span><br><span class="line">    AttachInfo mAttachInfo;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Retrieve a unique token identifying the window this view is attached to.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> Return the window's token for use in</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> WindowManager.LayoutParams#token WindowManager.LayoutParams.token&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> IBinder <span class="title">getWindowToken</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mAttachInfo != <span class="keyword">null</span> ? mAttachInfo.mWindowToken : <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><h2 id="ViewRootImpl"><a href="#ViewRootImpl" class="headerlink" title="ViewRootImpl"></a>ViewRootImpl</h2><p> 那么View的mAttachInfo又是如何赋值的呢？<br> 我们接着看第③root = new ViewRootImpl(view.getContext(), display);<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ViewRootImpl.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ViewRootImpl</span><span class="params">(Context context, Display display)</span> </span>&#123;</span><br><span class="line">        mWindowSession = WindowManagerGlobal.getWindowSession();</span><br><span class="line">        mWindow = <span class="keyword">new</span> W(<span class="keyword">this</span>);<span class="comment">//W是用于接收WmS通知的</span></span><br><span class="line">        mAttachInfo = <span class="keyword">new</span> View.AttachInfo(mWindowSession, mWindow, display, <span class="keyword">this</span>, mHandler, <span class="keyword">this</span>);</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//View.java</span></span><br><span class="line">        AttachInfo(IWindowSession session, IWindow window, Display display,</span><br><span class="line">                ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) &#123;</span><br><span class="line">            mSession = session;</span><br><span class="line">            mWindow = window;</span><br><span class="line">            mWindowToken = window.asBinder();<span class="comment">//即W类</span></span><br><span class="line">            mDisplay = display;</span><br><span class="line">            mViewRootImpl = viewRootImpl;</span><br><span class="line">            mHandler = handler;</span><br><span class="line">            mRootCallbacks = effectPlayer;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></p><p> 那么，如果这个View是ViewGroup，如何确保它的所有子View都能调用getWindowToken()获取到mAttachInfo的mWindowToken呢？<br> 这里我们涉及一点View的绘制流程看一下源码。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// ViewRootImpl.java</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">performTraversals</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (mFirst</span><br><span class="line">            <span class="comment">//第一次执行performTraversals时，会把自己的mAttachInfo关联到所有的子View</span></span><br><span class="line">            host.dispatchAttachedToWindow(mAttachInfo, <span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//ViewGroup.java</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dispatchAttachedToWindow</span><span class="params">(AttachInfo info, <span class="keyword">int</span> visibility)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">final</span> <span class="keyword">int</span> count = mChildrenCount;</span><br><span class="line">       <span class="keyword">final</span> View[] children = mChildren;</span><br><span class="line">       <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">           <span class="keyword">final</span> View child = children[i];</span><br><span class="line">           <span class="comment">//关联到所有子View</span></span><br><span class="line">           child.dispatchAttachedToWindow(info,visibility | (child.mViewFlags &amp; VISIBILITY_MASK));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//View.java</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dispatchAttachedToWindow</span><span class="params">(AttachInfo info, <span class="keyword">int</span> visibility)</span> </span>&#123;</span><br><span class="line">       mAttachInfo = info;</span><br><span class="line">        ```</span><br></pre></td></tr></table></figure></p><pre><code>}   </code></pre><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">## Session</span><br><span class="line"> 循序渐进，我们走到Session.</span><br><span class="line">```java</span><br><span class="line">    @Override</span><br><span class="line">    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,</span><br><span class="line">            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,</span><br><span class="line">            Rect outOutsets, InputChannel outInputChannel) &#123;</span><br><span class="line"></span><br><span class="line">        //window是在viewRootImpl创建的时候创建的，用来接收WmS消息</span><br><span class="line">        //mWindow = new W(this); 不要和token搞混了！</span><br><span class="line"></span><br><span class="line">        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,</span><br><span class="line">                outContentInsets, outStableInsets, outOutsets, outInputChannel);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="WmS"><a href="#WmS" class="headerlink" title="WmS"></a>WmS</h2><p> 最终，我们走进大魔王 faker WindowManagerService看看它到底是如何处理应用窗口以及子窗口。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//WindowManagerService.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">addWindow</span><span class="params">(Session session, IWindow client, <span class="keyword">int</span> seq,</span></span></span><br><span class="line"><span class="function"><span class="params">            WindowManager.LayoutParams attrs, <span class="keyword">int</span> viewVisibility, <span class="keyword">int</span> displayId,</span></span></span><br><span class="line"><span class="function"><span class="params">            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,</span></span></span><br><span class="line"><span class="function"><span class="params">            InputChannel outInputChannel)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//根据type检查窗口类型是否合法，如果是系统窗口类型，还需要进行权限检查</span></span><br><span class="line">    <span class="keyword">int</span> res = mPolicy.checkAddPermission(attrs, appOp);</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//如果是子窗口，根据attrs.token(也就是我们说的Window的token)检查父窗口是否合法</span></span><br><span class="line">    <span class="keyword">if</span> (type &gt;= FIRST_SUB_WINDOW &amp;&amp; type &lt;= LAST_SUB_WINDOW) &#123;</span><br><span class="line">                attachedWindow = windowForClientLocked(<span class="keyword">null</span>, attrs.token, <span class="keyword">false</span>);</span><br><span class="line">                <span class="keyword">if</span> (attachedWindow == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="comment">//父窗口不存在，直接抛出异常</span></span><br><span class="line">                     <span class="keyword">return</span> WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;</span><br><span class="line">                 &#125;</span><br><span class="line">                 <span class="comment">//如果父窗口也是子窗口，GG</span></span><br><span class="line">                 <span class="keyword">if</span> (attachedWindow.mAttrs.type &gt;= FIRST_SUB_WINDOW</span><br><span class="line">                       &amp;&amp; attachedWindow.mAttrs.type &lt;= LAST_SUB_WINDOW) &#123;</span><br><span class="line">                    <span class="keyword">return</span> WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;</span><br><span class="line">                 &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      ...</span><br><span class="line">    <span class="comment">//经过一系列的检查之后，最后会生成窗口在WmS中的表示WindowState，并且把LayoutParams赋值给WindowState的mAttrs</span></span><br><span class="line">    win = <span class="keyword">new</span> WindowState(<span class="keyword">this</span>, session, client, token, attachedWindow, appOp[<span class="number">0</span>], seq, attrs, viewVisibility, displayContent);  </span><br><span class="line">    ... </span><br><span class="line">    <span class="keyword">if</span> (addToken) &#123;</span><br><span class="line">     mTokenMap.put(attrs.token, token);</span><br><span class="line">    &#125; </span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//窗口添加成功，W类存放至mWindowMap</span></span><br><span class="line">     mWindowMap.put(client.asBinder(), win);   </span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> WindowState <span class="title">windowForClientLocked</span><span class="params">(Session session, IBinder client,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> throwOnError)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//client为attrs.token，即父类的W类对象</span></span><br><span class="line">        <span class="comment">//如果父类窗口添加成功，那么mWindowMap必定有此client</span></span><br><span class="line">        WindowState win = mWindowMap.get(client);</span><br><span class="line">        <span class="keyword">if</span> (win == <span class="keyword">null</span>) &#123;</span><br><span class="line">            RuntimeException ex = <span class="keyword">new</span> IllegalArgumentException(</span><br><span class="line">                    <span class="string">"Requested window "</span> + client + <span class="string">" does not exist"</span>);</span><br><span class="line">            <span class="keyword">if</span> (throwOnError) &#123;</span><br><span class="line">                <span class="keyword">throw</span> ex;<span class="comment">//抛出异常</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (session != <span class="keyword">null</span> &amp;&amp; win.mSession != session) &#123;</span><br><span class="line">            RuntimeException ex = <span class="keyword">new</span> IllegalArgumentException(</span><br><span class="line">                    <span class="string">"Requested window "</span> + client + <span class="string">" is in session "</span> +</span><br><span class="line">                    win.mSession + <span class="string">", not "</span> + session);</span><br><span class="line">            <span class="keyword">if</span> (throwOnError) &#123;</span><br><span class="line">                <span class="keyword">throw</span> ex;</span><br><span class="line">            &#125;</span><br><span class="line">            Slog.w(TAG_WM, <span class="string">"Failed looking up window"</span>, ex);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> win;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> 如果子窗口不合法（没有父窗口或者父窗口也是子窗口），那么将会抛出ADD_BAD_SUBWINDOW_TOKEN的异常，我们试试在Activity的onCreate()方法中去创建并显示一个popupWindow，至于崩溃原因结合上面所分析的，就能找出原因了。<br><img src="/Android-Window-Mechanism/window-07.png" alt="&quot;BadTokenException&quot;"><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ViewRootImpl.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setView</span><span class="params">(View view, WindowManager.LayoutParams attrs, View panelParentView)</span> </span>&#123;</span><br><span class="line">                    res = mWindowSession.addToDisplay(......);</span><br><span class="line">                    ...</span><br><span class="line">                    <span class="keyword">switch</span> (res) &#123;</span><br><span class="line">                        <span class="keyword">case</span> WindowManagerGlobal.ADD_BAD_APP_TOKEN:</span><br><span class="line">                        <span class="keyword">case</span> WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:</span><br><span class="line">                            <span class="keyword">throw</span> <span class="keyword">new</span> WindowManager.BadTokenException(</span><br><span class="line">                                    <span class="string">"Unable to add window -- token "</span> + attrs.token</span><br><span class="line">                                    + <span class="string">" is not valid; is your activity running?"</span>);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br></pre></td></tr></table></figure></p><p> 所以无论是应用窗口，还是子窗口，token 不能为空。否则会抛出异常，并且应用窗口的token 必须是某个 Activity 的 mToken，子窗口的token 必须是父窗口的 IWindow 对象。而且子窗口还需等父窗口添加成功之后才可添加到Window上。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p> 至此，Window的机制已经探索的有些眉目，阅读源码read the fuck source code能够更好的了解到Android的各种机制流程，而且能够熟悉源码编写的码神他们的编写风格，例如performLaunchActivity()就是Activity的整体流程开始，performTraversals（）就是开始绘制View，还有performMeasure()、performLayout()、performDraw()等等，并且探索了Window机制后，我们就能比较容易的上手View的绘制流程。</p><p>虽然说阅读源码在短时间内可能没办法体现出多大的作用，但是这是一种循序渐进积累和爆发的过程，只要不断的进步，量变终究能引起质变。</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/qian520ao/article/details/78555397" target="_blank" rel="noopener">Android Window 机制探索</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&amp;mid=2650241981&amp;idx=1&amp;sn=093141a89df53fb60637521893834138&amp;chksm=88638ad2bf1403c4961310c8771847484f06de4ae2fc6ec01da28a12edc7436c674b003dca9d&amp;scene=21#wechat_redirect" target="_blank" rel="noopener">Android进阶必备，Window机制探索</a><br>本文中提到的源码有两种方式可以查看：<br>• (1)通过SDK 可以查看framework/base/core下的Java和res源码<br>  比如Activity.java的源码：Sdk/sources/android-27/android/app/Activity.java<br>• (2)通过Google官网 <a href="https://android.googlesource.com,此方法需要翻墙" target="_blank" rel="noopener">https://android.googlesource.com,此方法需要翻墙</a>.</p><p>还未阅读<br><a href="https://www.jianshu.com/p/40a9c93b5a8d" target="_blank" rel="noopener">Android窗口机制（一）初识Android的窗口结构</a><br><a href="https://www.jianshu.com/p/e42b638944ae" target="_blank" rel="noopener">Android窗口机制（二）Window，PhoneWindow，DecorView，setContentView源码理解</a><br><a href="https://www.jianshu.com/p/6afb0c17df43" target="_blank" rel="noopener">Android窗口机制（三）Window和WindowManager的创建与Activity</a><br><a href="https://www.jianshu.com/p/9da7bfe18374" target="_blank" rel="noopener">Android窗口机制（四）ViewRootImpl与View和WindowManager</a><br><a href="https://www.jianshu.com/p/bac61386d9bf" target="_blank" rel="noopener">Android窗口机制（五）最终章：WindowManager.LayoutParams和Token以及其他窗口Dialog，Toast</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Window的概念&quot;&gt;&lt;a href=&quot;#Window的概念&quot; class=&quot;headerlink&quot; title=&quot;Window的概念&quot;&gt;&lt;/a&gt;Window的概念&lt;/h1&gt;&lt;p&gt;Android手机中所有的视图都是通过Window来呈现的，像常用的Activity
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Window" scheme="http://yoursite.com/categories/Android/Window/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Window" scheme="http://yoursite.com/tags/Window/"/>
    
  </entry>
  
  <entry>
    <title>Android的Log机制分析</title>
    <link href="http://yoursite.com/Android-Log-Analysis/"/>
    <id>http://yoursite.com/Android-Log-Analysis/</id>
    <published>2018-03-03T03:23:43.000Z</published>
    <updated>2018-08-07T06:57:21.274Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>LOG是广泛使用的用来记录程序执行过程的机制，它既可以用于程序调试，也可以用于产品运营中的事件记录。在Android系统中，提供了简单、便利的LOG机制，开发人员可以方便地使用。</p><h1 id="Log的使用"><a href="#Log的使用" class="headerlink" title="Log的使用"></a>Log的使用</h1><p> 本节介绍Kernel中内核态的printk和用户态的Log使用</p><h2 id="内核态Kernel-log的使用"><a href="#内核态Kernel-log的使用" class="headerlink" title="内核态Kernel log的使用"></a>内核态Kernel log的使用</h2><h3 id="printk的使用"><a href="#printk的使用" class="headerlink" title="printk的使用"></a>printk的使用</h3><p> Android是基于Linux内核的，所以Linux中的printk同样适用于Android.printk提供了8种日志级别<br> kernel/include/linux/kern_levels.h<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> __KERN_LEVELS_H__</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __KERN_LEVELS_H__</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_SOH<span class="meta-string">"\001"</span><span class="comment">/* ASCII Start Of Header */</span></span></span><br><span class="line">#define KERN_SOH_ASCII'\001'</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_EMERGKERN_SOH <span class="meta-string">"0"</span><span class="comment">/* system is unusable */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_ALERTKERN_SOH <span class="meta-string">"1"</span><span class="comment">/* action must be taken immediately */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_CRITKERN_SOH <span class="meta-string">"2"</span><span class="comment">/* critical conditions */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_ERRKERN_SOH <span class="meta-string">"3"</span><span class="comment">/* error conditions */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_WARNINGKERN_SOH <span class="meta-string">"4"</span><span class="comment">/* warning conditions */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_NOTICEKERN_SOH <span class="meta-string">"5"</span><span class="comment">/* normal but significant condition */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_INFOKERN_SOH <span class="meta-string">"6"</span><span class="comment">/* informational */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_DEBUGKERN_SOH <span class="meta-string">"7"</span><span class="comment">/* debug-level messages */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_DEFAULTKERN_SOH <span class="meta-string">"d"</span><span class="comment">/* the default kernel loglevel */</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Annotation for a "continued" line of log printout (only done after a</span></span><br><span class="line"><span class="comment"> * line that had no enclosing \n). Only to be used by core/arch code</span></span><br><span class="line"><span class="comment"> * during early bootup (a continued line is not SMP-safe otherwise).</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> KERN_CONT<span class="meta-string">""</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure></p><p> printk在代码中的<strong>使用方法</strong>：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">printk(KERN_ALERT<span class="string">"This is the log printed by printk in linux kernel space."</span>);</span><br></pre></td></tr></table></figure></p><p> 或者<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">printk(<span class="string">"1"</span> <span class="string">"This is the log printed by printk in linux kernel space."</span>);</span><br></pre></td></tr></table></figure></p><p> KERN_ALERT表示日志级别，后面紧跟着要格式化字符串。</p><p> printk的log<strong>查看方法</strong>：<br> 在Android系统中，printk输出的日志信息保存在/proc/kmsg中，要查看/proc/kmsg的内容,使用命令<br> cat /proc/kmsg<br> cat /dev/kmsg<br> adb logcat -b kernel</p><p> 查看当前日志<strong>打印级别</strong>：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/sys/kernel/printk</span><br></pre></td></tr></table></figure></p><p> 输出结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0 6 1 7</span><br></pre></td></tr></table></figure></p><p>四个值的含义：控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别</p><p> ★Tips: 如何调整Kernel log的日志级别?<br> 只有当printk打印信息时的loglevel小于console loglevel的值（优先级高于console loglevel），这些信息才会被打印到console上。比如当前的console loglevel是3,那么代码中loglevel为KERN_WARNING，KERN_NOTICE，KERN_INFO，KERN_DEBUG等的log将不会打印.(数字越小，日志优先级越高).</p><p> • 方法一：修改日志打印级别<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">"7 4 1 7"</span> &gt; /proc/sys/kernel/printk</span><br></pre></td></tr></table></figure></p><p> 这种方法的只能对当前环境有效，关机或者重启之后，立马失效.</p><p> • 方法二：修改printk.c里面的日志默认级别.kernel/msm-3.18/kernel/printk/printk.c<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">int console_printk[4] = &#123;</span><br><span class="line">CONSOLE_LOGLEVEL_DEFAULT,/* console_loglevel */</span><br><span class="line">MESSAGE_LOGLEVEL_DEFAULT,/* default_message_loglevel */</span><br><span class="line">CONSOLE_LOGLEVEL_MIN,/* minimum_console_loglevel */</span><br><span class="line">CONSOLE_LOGLEVEL_DEFAULT,/* default_console_loglevel */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p> 对应的修改console_loglevel,default_message_loglevel,minimum_console_loglevel,default_console_loglevel四个值即可.</p><h3 id="printk源码分析"><a href="#printk源码分析" class="headerlink" title="printk源码分析"></a>printk源码分析</h3><p> printk模块代码位置：kernel/msm-3.18/kernel/printk/<br> 待补充…<br> 参考资料：<br> <a href="http://book.51cto.com/art/200712/62871.htm" target="_blank" rel="noopener">printk打印消息机制</a><br> <a href="http://www.cnblogs.com/sunyubo/archive/2011/01/06/2282076.html" target="_blank" rel="noopener">printk实现分析</a><br> <a href="https://blog.csdn.net/skyflying2012/article/details/7970341" target="_blank" rel="noopener">内核printk的实现分析</a><br> <a href="https://www.cnblogs.com/pengdonglin137/p/4320965.html" target="_blank" rel="noopener">tiny4412 串口驱动分析二 — printk的实现</a><br> <a href="https://www.cnblogs.com/victor-ma/p/5332137.html" target="_blank" rel="noopener">内核日志及printk结构浅析</a></p><h3 id="dmesg-log"><a href="#dmesg-log" class="headerlink" title="dmesg log"></a>dmesg log</h3><ol><li><p>dmesg log介绍<br>dmesg里我们可以查看到开机信息，printk产生的信息等。‘dmesg’命令设备故障的诊断是非常重要的。在‘dmesg’命令的帮助下进行硬件的连接或断开连接操作时，我们可以看到硬件的检测或者断开连接的信息。</p></li><li><p>获取dmesg log<br>adb shell 后输入dmesg</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[   13.688706] i2c-msm-v2 78b6000.i2c: NACK: slave not responding, ensure its powered: msgs(n:1 cur:0 tx) bc(rx:0 tx:2) </span><br><span class="line">mode:FIFO slv_addr:0x1d MSTR_STS:0x0d1300c8 OPER:0x00000090</span><br><span class="line">[   13.703484] usb-type-c-pericom 2-001d: i2c write to [1d] failed -107</span><br><span class="line">[   13.709767] usb-type-c-pericom 2-001d: i2c access failed</span><br></pre></td></tr></table></figure><p>可以看到每行最开始显示的是一个综括号，里面的数字为timestamp，时间戳，该时间指示的系统从开机到现在的运行时间，单位为s 秒。</p></li><li><p>dmesg常用的方式<br>dmesg -c :在显示的同时，clean掉dmesg缓存中信息<br>dmesg -T :以当前时间的方式显示时间信息，而不是上面的那种毫秒的开机时间</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[Wed Jan  3 06:15:22 2018] i2c-msm-v2 78b6000.i2c: NACK: slave not responding, ensure its powered: msgs(n:1 cur:0 tx) bc</span><br><span class="line">(rx:0 tx:2) mode:FIFO slv_addr:0x1d MSTR_STS:0x0d1300c8 OPER:0x00000090</span><br><span class="line">[Wed Jan  3 06:15:22 2018] usb-type-c-pericom 2-001d: i2c write to [1d] failed -107</span><br><span class="line">[Wed Jan  3 06:15:22 2018] usb-type-c-pericom 2-001d: i2c access failed</span><br></pre></td></tr></table></figure><p>由于dmesg日志的输出不适合在一页中完全显示，因此我们使用管道（pipe）将其输出送到more或者less命令单页显示。<br>dmesg | more :<br>dmesg | less :<br>dmesg | grep xxx:<br>dmesg -T | more :<br>dmesg -T | less :<br>dmesg -T | grep xxx:<br>dmesg | grep sda: 列出所有被监测到的硬件<br>dmesg | head  -20：只输出前20行<br>dmesg | tail -20：只输出最后20行<br>搜索包含特定字符串的被检测到的硬件：<br>dmesg | grep -i usb<br>dmesg | grep -i dma<br>dmesg | grep -i tty<br>dmesg | grep -i memory</p></li></ol><h3 id="串口log"><a href="#串口log" class="headerlink" title="串口log"></a>串口log</h3><p> 常遇到无法开机的状况，这时由于Android还未起来，adb等均无法使用，此时有抓串口的必要。</p><p> 抓取串口log方式：</p><ol><li>Windows系统，使用串口调试工具：SSCOM 5.13.1</li><li>Linux操作系统：串口调试工具 minicom<br><a href="https://blog.csdn.net/u013398960/article/details/72967892" target="_blank" rel="noopener">Android串口log的获取</a><br><a href="https://www.cnblogs.com/wonux/p/5897127.html" target="_blank" rel="noopener">串口调试利器–Minicom配置及使用详解</a></li></ol><h2 id="用户态Log的使用"><a href="#用户态Log的使用" class="headerlink" title="用户态Log的使用"></a>用户态Log的使用</h2><p> Android系统在用户空间中提供了轻量级的logger日志系统，它是在内核中实现的一种设备驱动，与用户空间的logcat工具配合使用能够方便地跟踪调试程序.在Android系统中，分别为C/C++ 和Java语言提供两种不同的logger访问接口。C/C++日志接口一般是在编写硬件抽象层模块或者编写JNI方法时使用，而Java接口一般是在应用层编写APP时使用。</p><h3 id="C-C-模块Log的使用"><a href="#C-C-模块Log的使用" class="headerlink" title="C/C++模块Log的使用"></a>C/C++模块Log的使用</h3><p> Android系统中的C/C++日志接口是通过宏来使用的。在system/core/include/android/log.h定义了日志的级别：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Android log priority values, in ascending priority order.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> android_LogPriority &#123;</span><br><span class="line">  ANDROID_LOG_UNKNOWN = <span class="number">0</span>,</span><br><span class="line">  ANDROID_LOG_DEFAULT, <span class="comment">/* only for SetMinPriority() */</span></span><br><span class="line">  ANDROID_LOG_VERBOSE,</span><br><span class="line">  ANDROID_LOG_DEBUG,</span><br><span class="line">  ANDROID_LOG_INFO,</span><br><span class="line">  ANDROID_LOG_WARN,</span><br><span class="line">  ANDROID_LOG_ERROR,</span><br><span class="line">  ANDROID_LOG_FATAL,</span><br><span class="line">  ANDROID_LOG_SILENT, <span class="comment">/* only for SetMinPriority(); must be last */</span></span><br><span class="line">&#125; android_LogPriority;</span><br></pre></td></tr></table></figure></p><p> 对于老的Android版本<br> 在system/core/include/cutils/log.h中，定义了对应的宏，如对应于ANDROID_LOG_VERBOSE的宏LOGV：<br> …<br> 因此，如果要使用C/C++日志接口，只要定义自己的LOG_TAG宏和包含头文件system/core/include/cutils/log.h就可以了：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#define LOG_TAG "MY LOG TAG"</span></span><br><span class="line"><span class="comment">#include &lt;cutils/log.h&gt;</span></span><br></pre></td></tr></table></figure></p><pre><code>就可以了，例如使用LOGV：</code></pre><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LOGV(<span class="string">"This is the log printed by LOGV in android user space."</span>);</span><br></pre></td></tr></table></figure><p> Android8.1.0版本中，log模块进行了调整</p><p> ALOG</p><h3 id="Java模块Log的使用"><a href="#Java模块Log的使用" class="headerlink" title="Java模块Log的使用"></a>Java模块Log的使用</h3><p> Android系统中的Java日志接口.Android系统在Frameworks层中定义了Log接口（frameworks/base/core/java/android/util/Log.java）：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Log</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.v.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> VERBOSE = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.d.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEBUG = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.i.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INFO = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.w.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WARN = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.e.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ERROR = <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ASSERT = <span class="number">7</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Exception class used to capture a stack trace in &#123;<span class="doctag">@link</span> #wtf&#125;.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TerribleFailure</span> <span class="keyword">extends</span> <span class="title">Exception</span> </span>&#123;</span><br><span class="line">        TerribleFailure(String msg, Throwable cause) &#123; <span class="keyword">super</span>(msg, cause); &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Interface to handle terrible failures from &#123;<span class="doctag">@link</span> #wtf&#125;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TerribleFailureHandler</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onTerribleFailure</span><span class="params">(String tag, TerribleFailure what, <span class="keyword">boolean</span> system)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> TerribleFailureHandler sWtfHandler = <span class="keyword">new</span> TerribleFailureHandler() &#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onTerribleFailure</span><span class="params">(String tag, TerribleFailure what, <span class="keyword">boolean</span> system)</span> </span>&#123;</span><br><span class="line">                RuntimeInit.wtf(tag, what, system);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Log</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Send a &#123;<span class="doctag">@link</span> #VERBOSE&#125; log message.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> println_native(LOG_ID_MAIN, VERBOSE, tag, msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Send a &#123;<span class="doctag">@link</span> #VERBOSE&#125; log message and log the exception.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tr An exception to log</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg, Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   ....</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Checks to see whether or not a log for the specified tag is loggable at the specified level.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *  The default level of any tag is set to INFO. This means that any level above and including</span></span><br><span class="line"><span class="comment">     *  INFO will be logged. Before you make any calls to a logging method you should check to see</span></span><br><span class="line"><span class="comment">     *  if your tag should be logged. You can change the default level by setting a system property:</span></span><br><span class="line"><span class="comment">     *      'setprop log.tag.&amp;lt;YOUR_LOG_TAG&gt; &amp;lt;LEVEL&gt;'</span></span><br><span class="line"><span class="comment">     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will</span></span><br><span class="line"><span class="comment">     *  turn off all logging for your tag. You can also create a local.prop file that with the</span></span><br><span class="line"><span class="comment">     *  following in it:</span></span><br><span class="line"><span class="comment">     *      'log.tag.&amp;lt;YOUR_LOG_TAG&gt;=&amp;lt;LEVEL&gt;'</span></span><br><span class="line"><span class="comment">     *  and place that in /data/local.prop.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag The tag to check.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> level The level to check.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> Whether or not that this is allowed to be logged.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException is thrown if the tag.length() &gt; 23</span></span><br><span class="line"><span class="comment">     *         for Nougat (7.0) releases (API &lt;= 23) and prior, there is no</span></span><br><span class="line"><span class="comment">     *         tag limit of concern after this API level.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">isLoggable</span><span class="params">(String tag, <span class="keyword">int</span> level)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Send a &#123;@link #WARN&#125; log message and log the exception.</span></span><br><span class="line"><span class="comment">     * @param tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * @param tr An exception to log</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">w</span><span class="params">(String tag, Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> printlns(LOG_ID_MAIN, WARN, tag, <span class="string">""</span>, tr);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Send an &#123;<span class="doctag">@link</span> #ERROR&#125; log message.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">e</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> println_native(LOG_ID_MAIN, ERROR, tag, msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Send a &#123;<span class="doctag">@link</span> #ERROR&#125; log message and log the exception.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tr An exception to log</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">e</span><span class="params">(String tag, String msg, Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> printlns(LOG_ID_MAIN, ERROR, tag, msg, tr);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * What a Terrible Failure: Report a condition that should never happen.</span></span><br><span class="line"><span class="comment">     * The error will always be logged at level ASSERT with the call stack.</span></span><br><span class="line"><span class="comment">     * Depending on system configuration, a report may be added to the</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@link</span> android.os.DropBoxManager&#125; and/or the process may be terminated</span></span><br><span class="line"><span class="comment">     * immediately with an error dialog.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wtf</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> wtf(LOG_ID_MAIN, tag, msg, <span class="keyword">null</span>, <span class="keyword">false</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Like &#123;<span class="doctag">@link</span> #wtf(String, String)&#125;, but also writes to the log the full</span></span><br><span class="line"><span class="comment">     * call stack.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wtfStack</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> wtf(LOG_ID_MAIN, tag, msg, <span class="keyword">null</span>, <span class="keyword">true</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * What a Terrible Failure: Report an exception that should never happen.</span></span><br><span class="line"><span class="comment">     * Similar to &#123;<span class="doctag">@link</span> #wtf(String, String)&#125;, with an exception to log.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tr An exception to log.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wtf</span><span class="params">(String tag, Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, <span class="keyword">false</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * What a Terrible Failure: Report an exception that should never happen.</span></span><br><span class="line"><span class="comment">     * Similar to &#123;<span class="doctag">@link</span> #wtf(String, Throwable)&#125;, with a message as well.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tr An exception to log.  May be null.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wtf</span><span class="params">(String tag, String msg, Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> wtf(LOG_ID_MAIN, tag, msg, tr, <span class="keyword">false</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">wtf</span><span class="params">(<span class="keyword">int</span> logId, String tag, String msg, Throwable tr, <span class="keyword">boolean</span> localStack,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> system)</span> </span>&#123;</span><br><span class="line">        TerribleFailure what = <span class="keyword">new</span> TerribleFailure(msg, tr);</span><br><span class="line">        <span class="comment">// Only mark this as ERROR, do not use ASSERT since that should be</span></span><br><span class="line">        <span class="comment">// reserved for cases where the system is guaranteed to abort.</span></span><br><span class="line">        <span class="comment">// The onTerribleFailure call does not always cause a crash.</span></span><br><span class="line">        <span class="keyword">int</span> bytes = printlns(logId, ERROR, tag, msg, localStack ? what : tr);</span><br><span class="line">        sWtfHandler.onTerribleFailure(tag, what, system);</span><br><span class="line">        <span class="keyword">return</span> bytes;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">wtfQuiet</span><span class="params">(<span class="keyword">int</span> logId, String tag, String msg, <span class="keyword">boolean</span> system)</span> </span>&#123;</span><br><span class="line">        TerribleFailure what = <span class="keyword">new</span> TerribleFailure(msg, <span class="keyword">null</span>);</span><br><span class="line">        sWtfHandler.onTerribleFailure(tag, what, system);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Sets the terrible failure handler, for testing.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the old handler</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> TerribleFailureHandler <span class="title">setWtfHandler</span><span class="params">(TerribleFailureHandler handler)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (handler == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">"handler == null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        TerribleFailureHandler oldHandler = sWtfHandler;</span><br><span class="line">        sWtfHandler = handler;</span><br><span class="line">        <span class="keyword">return</span> oldHandler;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Handy function to get a loggable stack trace from a Throwable</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tr An exception to log</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">getStackTraceString</span><span class="params">(Throwable tr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (tr == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// This is to reduce the amount of log spew that apps do in the non-error</span></span><br><span class="line">        <span class="comment">// condition of the network being unavailable.</span></span><br><span class="line">        Throwable t = tr;</span><br><span class="line">        <span class="keyword">while</span> (t != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (t <span class="keyword">instanceof</span> UnknownHostException) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            t = t.getCause();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        StringWriter sw = <span class="keyword">new</span> StringWriter();</span><br><span class="line">        PrintWriter pw = <span class="keyword">new</span> FastPrintWriter(sw, <span class="keyword">false</span>, <span class="number">256</span>);</span><br><span class="line">        tr.printStackTrace(pw);</span><br><span class="line">        pw.flush();</span><br><span class="line">        <span class="keyword">return</span> sw.toString();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Low-level logging call.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> priority The priority/type of this log message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment">     *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> The number of bytes written.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">println</span><span class="params">(<span class="keyword">int</span> priority, String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> println_native(LOG_ID_MAIN, priority, tag, msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_MAIN = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_RADIO = <span class="number">1</span>;</span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_EVENTS = <span class="number">2</span>;</span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_SYSTEM = <span class="number">3</span>;</span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_CRASH = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">println_native</span><span class="params">(<span class="keyword">int</span> bufID,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> priority, String tag, String msg)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Return the maximum payload the log daemon accepts without truncation.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> LOGGER_ENTRY_MAX_PAYLOAD.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">logger_entry_max_payload_native</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Helper function for long messages. Uses the LineBreakBufferedWriter to break</span></span><br><span class="line"><span class="comment">     * up long messages and stacktraces along newlines, but tries to write in large</span></span><br><span class="line"><span class="comment">     * chunks. This is to avoid truncation.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">printlns</span><span class="params">(<span class="keyword">int</span> bufID, <span class="keyword">int</span> priority, String tag, String msg,</span></span></span><br><span class="line"><span class="function"><span class="params">            Throwable tr)</span> </span>&#123;</span><br><span class="line">        ImmediateLogWriter logWriter = <span class="keyword">new</span> ImmediateLogWriter(bufID, priority, tag);</span><br><span class="line">        <span class="comment">// Acceptable buffer size. Get the native buffer size, subtract two zero terminators,</span></span><br><span class="line">        <span class="comment">// and the length of the tag.</span></span><br><span class="line">        <span class="comment">// Note: we implicitly accept possible truncation for Modified-UTF8 differences. It</span></span><br><span class="line">        <span class="comment">//       is too expensive to compute that ahead of time.</span></span><br><span class="line">        <span class="keyword">int</span> bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD    <span class="comment">// Base.</span></span><br><span class="line">                - <span class="number">2</span>                                                <span class="comment">// Two terminators.</span></span><br><span class="line">                - (tag != <span class="keyword">null</span> ? tag.length() : <span class="number">0</span>)                 <span class="comment">// Tag length.</span></span><br><span class="line">                - <span class="number">32</span>;                                              <span class="comment">// Some slack.</span></span><br><span class="line">        <span class="comment">// At least assume you can print *some* characters (tag is not too large).</span></span><br><span class="line">        bufferSize = Math.max(bufferSize, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">        LineBreakBufferedWriter lbbw = <span class="keyword">new</span> LineBreakBufferedWriter(logWriter, bufferSize);</span><br><span class="line"></span><br><span class="line">        lbbw.println(msg);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (tr != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// This is to reduce the amount of log spew that apps do in the non-error</span></span><br><span class="line">            <span class="comment">// condition of the network being unavailable.</span></span><br><span class="line">            Throwable t = tr;</span><br><span class="line">            <span class="keyword">while</span> (t != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (t <span class="keyword">instanceof</span> UnknownHostException) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (t <span class="keyword">instanceof</span> DeadSystemException) &#123;</span><br><span class="line">                    lbbw.println(<span class="string">"DeadSystemException: The system died; "</span></span><br><span class="line">                            + <span class="string">"earlier logs will point to the root cause"</span>);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                t = t.getCause();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (t == <span class="keyword">null</span>) &#123;</span><br><span class="line">                tr.printStackTrace(lbbw);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        lbbw.flush();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> logWriter.getWritten();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid</span></span><br><span class="line"><span class="comment">     * a JNI call during logging.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">PreloadHolder</span> </span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">int</span> LOGGER_ENTRY_MAX_PAYLOAD =</span><br><span class="line">                logger_entry_max_payload_native();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Helper class to write to the logcat. Different from LogWriter, this writes</span></span><br><span class="line"><span class="comment">     * the whole given buffer and does not break along newlines.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ImmediateLogWriter</span> <span class="keyword">extends</span> <span class="title">Writer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> bufID;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> priority;</span><br><span class="line">        <span class="keyword">private</span> String tag;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> written = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Create a writer that immediately writes to the log, using the given</span></span><br><span class="line"><span class="comment">         * parameters.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">ImmediateLogWriter</span><span class="params">(<span class="keyword">int</span> bufID, <span class="keyword">int</span> priority, String tag)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.bufID = bufID;</span><br><span class="line">            <span class="keyword">this</span>.priority = priority;</span><br><span class="line">            <span class="keyword">this</span>.tag = tag;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getWritten</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> written;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">(<span class="keyword">char</span>[] cbuf, <span class="keyword">int</span> off, <span class="keyword">int</span> len)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Note: using String here has a bit of overhead as a Java object is created,</span></span><br><span class="line">            <span class="comment">//       but using the char[] directly is not easier, as it needs to be translated</span></span><br><span class="line">            <span class="comment">//       to a C char[] for logging.</span></span><br><span class="line">            written += println_native(bufID, priority, tag, <span class="keyword">new</span> String(cbuf, off, len));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">flush</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Ignored.</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Ignored.</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 因此，如果要使用Java日志接口，只要在类中定义的LOG_TAG常量和引用android.util.Log就可以了：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String LOG_TAG = <span class="string">"MY_LOG_TAG"</span>;</span><br><span class="line">Log.i(LOG_TAG, <span class="string">"This is the log printed by Log.i in android user space."</span>);</span><br></pre></td></tr></table></figure></p><p> 用户态log查看工具： logcat</p><p> ★ Tips：logcat输出log的级别控制</p><h2 id="logcat的使用"><a href="#logcat的使用" class="headerlink" title="logcat的使用"></a>logcat的使用</h2><p> 参考资料<br> <a href="https://zhuanlan.zhihu.com/p/24372024" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/24372024</a></p><h1 id="Framework中Log功能解析"><a href="#Framework中Log功能解析" class="headerlink" title="Framework中Log功能解析"></a>Framework中Log功能解析</h1><p> AP侧的Log系统根据log类型，分为main,system,radio,crash,events.<br> frameworks/base/core/java/android/util/Log.java //用于记录main log<br> frameworks/base/core/java/android/util/Slog.java //用于记录framework log<br> frameworks/base/telephony/java/android/telephony/Rlog.java //用于记录radio log<br> frameworks/base/core/java/com/android/internal/os/RuntimeInit.java //内部记录了crash log<br> frameworks/base/core/java/android/util/EventLog.java //用于Event log的使用</p><p> 上述5种类型的log可以分为3大类：<br> 第一类：main/system/radio<br> 第二类：event<br> 第三类：crash</p><h2 id="main-system-radio-log"><a href="#main-system-radio-log" class="headerlink" title="main/system/radio log"></a>main/system/radio log</h2><p> 从Log.java,Slog.java,Rlog.java三个文件来看，这三种log最大的区别就是LOG_ID不一样，即在framework侧已经区分了写入不同的缓冲区,如下：<br> Log.java,通过println_native写入LOG_ID_MAIN缓冲区<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Send a &#123;<span class="doctag">@link</span> #VERBOSE&#125; log message.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment"> *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> println_native(LOG_ID_MAIN, VERBOSE, tag, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> Slog.java,通过println_native写入LOG_ID_SYSTEM缓冲区<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> Rlog.java,通过println_native写入LOG_ID_RADIO缓冲区<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> Slog和Rlog都是调用Log里面的println_native函数，只是缓冲区ID不一样而已.<br> 对于这三类log，主要分析Log.java.Log主要实现以下函数：<br>  • 定义了Log的5中优先级,分别是 VERBOSE &gt; DEBUG &gt; INFO &gt; WARN &gt; ERROR &gt; ASSERT,数字越大优先级越高<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.v.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> VERBOSE = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.d.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEBUG = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.i.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INFO = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.w.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WARN = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method; use Log.e.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ERROR = <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Priority constant for the println method.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ASSERT = <span class="number">7</span>;</span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line"> • 带Tag和msg两个参数的实现,最后通过JNI调用println_native函数实现，后面继续分析.</span><br><span class="line">```java</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> println_native(LOG_ID_MAIN, VERBOSE, tag, msg);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p> • 带Tag,msg以及Throwable三个参数的实现,<a href="https://github.com/EricChows/JDK-1.8-sourcecode/blob/master/java/lang/Throwable.java" target="_blank" rel="noopener">可以在这里查看Throwable的源码</a>，多了一个异常参数.在输出log信息的同时，如果报了异常，同时将异常信息输出.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">v</span><span class="params">(String tag, String msg, Throwable tr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 通过printlns函数实现,继续追踪printlns函数如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Helper function for long messages. Uses the LineBreakBufferedWriter to break</span></span><br><span class="line"><span class="comment"> * up long messages and stacktraces along newlines, but tries to write in large</span></span><br><span class="line"><span class="comment"> * chunks. This is to avoid truncation.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">printlns</span><span class="params">(<span class="keyword">int</span> bufID, <span class="keyword">int</span> priority, String tag, String msg,</span></span></span><br><span class="line"><span class="function"><span class="params">        Throwable tr)</span> </span>&#123;</span><br><span class="line">    ImmediateLogWriter logWriter = <span class="keyword">new</span> ImmediateLogWriter(bufID, priority, tag);</span><br><span class="line">    <span class="comment">// Acceptable buffer size. Get the native buffer size, subtract two zero terminators,</span></span><br><span class="line">    <span class="comment">// and the length of the tag.</span></span><br><span class="line">    <span class="comment">// Note: we implicitly accept possible truncation for Modified-UTF8 differences. It</span></span><br><span class="line">    <span class="comment">//       is too expensive to compute that ahead of time.</span></span><br><span class="line">    <span class="keyword">int</span> bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD    <span class="comment">// Base.</span></span><br><span class="line">            - <span class="number">2</span>                                                <span class="comment">// Two terminators.</span></span><br><span class="line">            - (tag != <span class="keyword">null</span> ? tag.length() : <span class="number">0</span>)                 <span class="comment">// Tag length.</span></span><br><span class="line">            - <span class="number">32</span>;                                              <span class="comment">// Some slack.</span></span><br><span class="line">    <span class="comment">// At least assume you can print *some* characters (tag is not too large).</span></span><br><span class="line">    bufferSize = Math.max(bufferSize, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    LineBreakBufferedWriter lbbw = <span class="keyword">new</span> LineBreakBufferedWriter(logWriter, bufferSize);</span><br><span class="line"></span><br><span class="line">    lbbw.println(msg); <span class="comment">//println函数后面还是调用了println_native</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (tr != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// This is to reduce the amount of log spew that apps do in the non-error</span></span><br><span class="line">        <span class="comment">// condition of the network being unavailable.</span></span><br><span class="line">        Throwable t = tr;</span><br><span class="line">        <span class="keyword">while</span> (t != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (t <span class="keyword">instanceof</span> UnknownHostException) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (t <span class="keyword">instanceof</span> DeadSystemException) &#123;</span><br><span class="line">                lbbw.println(<span class="string">"DeadSystemException: The system died; "</span></span><br><span class="line">                        + <span class="string">"earlier logs will point to the root cause"</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            t = t.getCause();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (t == <span class="keyword">null</span>) &#123;</span><br><span class="line">            tr.printStackTrace(lbbw);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    lbbw.flush();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> logWriter.getWritten();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 由于带有异常信息，一般异常信息都很长很大，而且里面有换行符,而每条log信息长度最大只能是4k个字节，所以为了避免信息被截断，在写入缓冲区之前，需要对这类信息进行预处理,也就是进行切割，分成多行显示.使用LineBreakBufferedWriter类刚好符合这样的要求，将这样的msg，识别出换行符之后，切割成多块，分行显示.</p><p> • 实现println函数,也就是说我们也可以用Log.println函数来实现log的输出，不过使用的比较少.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Low-level logging call.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> priority The priority/type of this log message</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> tag Used to identify the source of a log message.  It usually identifies</span></span><br><span class="line"><span class="comment"> *        the class or activity where the log call occurs.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> msg The message you would like logged.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> The number of bytes written.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">println</span><span class="params">(<span class="keyword">int</span> priority, String tag, String msg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> println_native(LOG_ID_MAIN, priority, tag, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> • 实现了wtf的多个重载方法,wtf的解释是What a Terrible Failure: Report a condition that should never happen.The error will always be logged at level ASSERT with the call stack.Depending on system configuration, a report may be added to the { @link android.os.DropBoxManager } and/or the process may be terminated immediately with an error dialog. 表示此时遇到了一个不可能发生，严重而又无法挽救的情况,对应的是ASSERT级别的日志,一般用于系统应用中，会用在Dropbox日志以及立即杀死进程弹框等场景下.这些应用留给后面讲Dropbox log以及ANR等具体使用的时候现场分析.</p><h2 id="Event-log"><a href="#Event-log" class="headerlink" title="Event log"></a>Event log</h2><p> 第二类的EventLog,可以在Java和C/C++代码中使用，用于特殊事件的打点,这里说的事件不仅仅值的输入事件,还包括特殊场景的特殊状态，比如Activity，Window等状态.由于Eventlog的实现机制和上面第一类的不一样，下面的章节会单独将Eventlog作为一节来讲，请往下面章节看.本节主要分析EventLog.java的功能.</p><p> a)实现writeEvent函数，通过JNI，调用到底层的代码，最终将event写入到<strong>/dev/log/events(之前版本写入的是这里，现在的8.1.0版本不再是这里了)</strong><br> b)实现readEvents函数，通过JNI,调用到底层代码, 具体使用场景还不没找,提供了方法来读取对应type的eventlog<br> c)实现getTagName函数，通过给出的ID，返回Tag的名称，主要通过在system/etc/event-log-tags文件里面进行查找实现，供Java侧其他模块调用<br> d)实现getTagCode函数，通过给出的Tag名称，返回Tag的ID，主要通过在system/etc/event-log-tags文件里面进行查找实现，供Java侧其他模块调用<br> e)实现内部静态类Event，这个是8.1.0之前的老版本的EventLog的实现.暂不关注.</p><h2 id="crash-log"><a href="#crash-log" class="headerlink" title="crash log"></a>crash log</h2><p> 对于第三类crash log.用于捕获应用的异常.<br> RuntimeInit.java是通过app_server启动，看frameworks/base/cmds/app_process/app_main.cpp代码如下:<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* <span class="keyword">const</span> argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">...</span><br><span class="line">    <span class="keyword">if</span> (zygote) &#123;</span><br><span class="line">        runtime.start(<span class="string">"com.android.internal.os.ZygoteInit"</span>, args, zygote);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (className) &#123;</span><br><span class="line">        runtime.start(<span class="string">"com.android.internal.os.RuntimeInit"</span>, args, zygote);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">"Error: no class name or --zygote supplied.\n"</span>);</span><br><span class="line">        app_usage();</span><br><span class="line">        LOG_ALWAYS_FATAL(<span class="string">"app_process: no class name or --zygote supplied."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 我们来看一下frameworks/base/core/java/com/android/internal/os/RuntimeInit.java,从上面代码可以看出，RuntimeInit是所有APP 进程的入口，AP侧的所有进程都是从这里启动，负责进程初始化的，继续看RuntimeInit代码实现的功能,在RuntimeInit.java中Clog_e函数里面可以看到crash log是输出到LOG_ID_CRASH缓冲区里面的,当然应用crash时的日志级别也是ERROR级的，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">Clog_e</span><span class="params">(String tag, String msg, Throwable tr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> RuntimeInit里面有两个内部类LoggingHandler和KillApplicationHandler分别用于捕获线程和Application的异常信息并输出log.下面分别来看一下<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Logs a message when a thread encounters an uncaught exception. By</span></span><br><span class="line"><span class="comment"> * default, &#123;<span class="doctag">@link</span> KillApplicationHandler&#125; will terminate this process later,</span></span><br><span class="line"><span class="comment"> * but apps can override that behavior.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">LoggingHandler</span> <span class="keyword">implements</span> <span class="title">Thread</span>.<span class="title">UncaughtExceptionHandler</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">uncaughtException</span><span class="params">(Thread t, Throwable e)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Don't re-enter if KillApplicationHandler has already run</span></span><br><span class="line">        <span class="keyword">if</span> (mCrashing) <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span> (mApplicationObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// The "FATAL EXCEPTION" string is still used on Android even though</span></span><br><span class="line">            <span class="comment">// apps can set a custom UncaughtExceptionHandler that renders uncaught</span></span><br><span class="line">            <span class="comment">// exceptions non-fatal.</span></span><br><span class="line">            Clog_e(TAG, <span class="string">"*** FATAL EXCEPTION IN SYSTEM PROCESS: "</span> + t.getName(), e);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            StringBuilder message = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">            <span class="comment">// The "FATAL EXCEPTION" string is still used on Android even though</span></span><br><span class="line">            <span class="comment">// apps can set a custom UncaughtExceptionHandler that renders uncaught</span></span><br><span class="line">            <span class="comment">// exceptions non-fatal.</span></span><br><span class="line">            message.append(<span class="string">"FATAL EXCEPTION: "</span>).append(t.getName()).append(<span class="string">"\n"</span>);</span><br><span class="line">            <span class="keyword">final</span> String processName = ActivityThread.currentProcessName();</span><br><span class="line">            <span class="keyword">if</span> (processName != <span class="keyword">null</span>) &#123;</span><br><span class="line">                message.append(<span class="string">"Process: "</span>).append(processName).append(<span class="string">", "</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            message.append(<span class="string">"PID: "</span>).append(Process.myPid());</span><br><span class="line">            Clog_e(TAG, message.toString(), e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 从代码中可以看到，当ApplicationObject为空的时候，表示Binder出了问题，此时应该是系统进程出问题了，会打印，*** FATAL EXCEPTION IN SYSTEM PROCESS: 等字符串.<br> 如果线程遇到了其他异常，会打印FATAL EXCEPTION:，以及当前进程名称，进程ID号.如下有一个实例：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">06-14 11:08:30.924 10048  2484  2585 E AndroidRuntime: FATAL EXCEPTION: AsyncTask #1</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: Process: com.emoji.keyboard.touchpal.go, PID: <span class="number">2484</span></span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:<span class="number">391</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:<span class="number">376</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:<span class="number">167</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at com.facebook.internal.FetchedAppSettingsManager$<span class="number">1</span>.run(SourceFile:<span class="number">106</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:<span class="number">1162</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:<span class="number">636</span>)</span><br><span class="line"><span class="number">06</span>-<span class="number">14</span> <span class="number">11</span>:<span class="number">08</span>:<span class="number">30.924</span> <span class="number">10048</span>  <span class="number">2484</span>  <span class="number">2585</span> E AndroidRuntime: at java.lang.Thread.run(Thread.java:<span class="number">764</span>)</span><br></pre></td></tr></table></figure></p><p> 继续看KillApplicationHandler的异常捕获:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Handle application death from an uncaught exception.  The framework</span></span><br><span class="line"><span class="comment"> * catches these for the main threads, so this should only matter for</span></span><br><span class="line"><span class="comment"> * threads created by applications.  Before this method runs,</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> LoggingHandler&#125; will already have logged details.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">KillApplicationHandler</span> <span class="keyword">implements</span> <span class="title">Thread</span>.<span class="title">UncaughtExceptionHandler</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">uncaughtException</span><span class="params">(Thread t, Throwable e)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// Don't re-enter -- avoid infinite loops if crash-reporting crashes.</span></span><br><span class="line">            <span class="keyword">if</span> (mCrashing) <span class="keyword">return</span>;</span><br><span class="line">            mCrashing = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Try to end profiling. If a profiler is running at this point, and we kill the</span></span><br><span class="line">            <span class="comment">// process (below), the in-memory buffer will be lost. So try to stop, which will</span></span><br><span class="line">            <span class="comment">// flush the buffer. (This makes method trace profiling useful to debug crashes.)</span></span><br><span class="line">            <span class="keyword">if</span> (ActivityThread.currentActivityThread() != <span class="keyword">null</span>) &#123;</span><br><span class="line">                ActivityThread.currentActivityThread().stopProfiling();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Bring up crash dialog, wait for it to be dismissed</span></span><br><span class="line">            ActivityManager.getService().handleApplicationCrash(</span><br><span class="line">                    mApplicationObject, <span class="keyword">new</span> ApplicationErrorReport.ParcelableCrashInfo(e));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t2) &#123;</span><br><span class="line">            <span class="keyword">if</span> (t2 <span class="keyword">instanceof</span> DeadObjectException) &#123;</span><br><span class="line">                <span class="comment">// System process is dead; ignore</span></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Clog_e(TAG, <span class="string">"Error reporting crash"</span>, t2);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Throwable t3) &#123;</span><br><span class="line">                    <span class="comment">// Even Clog_e() fails!  Oh well.</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// Try everything to make sure this process goes away.</span></span><br><span class="line">            Process.killProcess(Process.myPid());</span><br><span class="line">            System.exit(<span class="number">10</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 可以看到，调用了ActivityManager中的handleApplicationCrash方法来处理,如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Used by &#123;<span class="doctag">@link</span> com.android.internal.os.RuntimeInit&#125; to report when an application crashes.</span></span><br><span class="line"><span class="comment"> * The application process will exit immediately after this call returns.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app object of the crashing app, null for the system server</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> crashInfo describing the exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleApplicationCrash</span><span class="params">(IBinder app,</span></span></span><br><span class="line"><span class="function"><span class="params">        ApplicationErrorReport.ParcelableCrashInfo crashInfo)</span> </span>&#123;</span><br><span class="line">    ProcessRecord r = findAppProcess(app, <span class="string">"Crash"</span>);</span><br><span class="line">    <span class="keyword">final</span> String processName = app == <span class="keyword">null</span> ? <span class="string">"system_server"</span></span><br><span class="line">            : (r == <span class="keyword">null</span> ? <span class="string">"unknown"</span> : r.processName);</span><br><span class="line"></span><br><span class="line">    handleApplicationCrashInner(<span class="string">"crash"</span>, r, processName, crashInfo);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Native crash reporting uses this inner version because it needs to be somewhat</span></span><br><span class="line"><span class="comment"> * decoupled from the AM-managed cleanup lifecycle</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">handleApplicationCrashInner</span><span class="params">(String eventType, ProcessRecord r, String processName,</span></span></span><br><span class="line"><span class="function"><span class="params">        ApplicationErrorReport.CrashInfo crashInfo)</span> </span>&#123;</span><br><span class="line">    EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),</span><br><span class="line">            UserHandle.getUserId(Binder.getCallingUid()), processName,</span><br><span class="line">            r == <span class="keyword">null</span> ? -<span class="number">1</span> : r.info.flags,</span><br><span class="line">            crashInfo.exceptionClassName,</span><br><span class="line">            crashInfo.exceptionMessage,</span><br><span class="line">            crashInfo.throwFileName,</span><br><span class="line">            crashInfo.throwLineNumber);</span><br><span class="line"></span><br><span class="line">    addErrorToDropBox(eventType, r, processName, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, crashInfo);</span><br><span class="line"></span><br><span class="line">    mAppErrors.crashApplication(r, crashInfo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 可以看到发生crash时，也往event log里面写入了crash事件，接着干两件事<br> 一：addErrorToDropBox,addErrorToDropBox函数是将crash/WTF/ANR/Low on memory 等信息收集并拼接处头部信息后输出到dropbox文件中.<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Write a description of an error (crash, WTF, ANR) to the drop box.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> eventType to include in the drop box tag ("crash", "wtf", etc.)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> process which caused the error, null means the system server</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> activity which triggered the error, null if unknown</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> parent activity related to the error, null if unknown</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> subject line related to the error, null if absent</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> report in long form describing the error, null if absent</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dataFile text file to include in the report, null if none</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> crashInfo giving an application stack trace, null if absent</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addErrorToDropBox</span><span class="params">(String eventType,</span></span></span><br><span class="line"><span class="function"><span class="params">        ProcessRecord process, String processName, ActivityRecord activity,</span></span></span><br><span class="line"><span class="function"><span class="params">        ActivityRecord parent, String subject,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">final</span> String report, <span class="keyword">final</span> File dataFile,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">final</span> ApplicationErrorReport.CrashInfo crashInfo)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// NOTE -- this must never acquire the ActivityManagerService lock,</span></span><br><span class="line">    <span class="comment">// otherwise the watchdog may be prevented from resetting the system.</span></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 二：mAppErrors.crashApplication(r, crashInfo);我们到AppErrors.java中看看crashApplication,可以看到主要目的是对发生crash的APP,杀死进程后弹出一个dialog框告知用户or not.详细的处理逻辑见crashApplicationInner函数.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Bring up the "unexpected error" dialog box for a crashing app.</span></span><br><span class="line"><span class="comment"> * Deal with edge cases (intercepts from instrumented applications,</span></span><br><span class="line"><span class="comment"> * ActivityController, error intent receivers, that sort of thing).</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> r the application crashing</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> crashInfo describing the failure</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">crashApplication</span><span class="params">(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> callingPid = Binder.getCallingPid();</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> callingUid = Binder.getCallingUid();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">long</span> origId = Binder.clearCallingIdentity();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        crashApplicationInner(r, crashInfo, callingPid, callingUid);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        Binder.restoreCallingIdentity(origId);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="JNI中log功能解析"><a href="#JNI中log功能解析" class="headerlink" title="JNI中log功能解析"></a>JNI中log功能解析</h1><p> 在上一节中总结了Framework层中提供了一些log方法供Java侧来使用，最后都调用到了frameworks/base/core/java/android/util/Log.java里面的println_native这个native函数，本节就开始往下追踪println_native函数的实现.<br> 再讲两个Log.java里面我们看到的细节：<br> 一、log总共有5个缓冲区,如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_MAIN = <span class="number">0</span>;</span><br><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_RADIO = <span class="number">1</span>;</span><br><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_EVENTS = <span class="number">2</span>;</span><br><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_SYSTEM = <span class="number">3</span>;</span><br><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOG_ID_CRASH = <span class="number">4</span>;</span><br></pre></td></tr></table></figure></p><p> 二、共调用了三个native方法,println_native,logger_entry_max_payload_native,isLoggable<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** <span class="doctag">@hide</span> */</span> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">println_native</span><span class="params">(<span class="keyword">int</span> bufID,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">int</span> priority, String tag, String msg)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Return the maximum payload the log daemon accepts without truncation.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> LOGGER_ENTRY_MAX_PAYLOAD.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">logger_entry_max_payload_native</span><span class="params">()</span></span>;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Checks to see whether or not a log for the specified tag is loggable at the specified level.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *  The default level of any tag is set to INFO. This means that any level above and including</span></span><br><span class="line"><span class="comment"> *  INFO will be logged. Before you make any calls to a logging method you should check to see</span></span><br><span class="line"><span class="comment"> *  if your tag should be logged. You can change the default level by setting a system property:</span></span><br><span class="line"><span class="comment"> *      'setprop log.tag.&amp;lt;YOUR_LOG_TAG&gt; &amp;lt;LEVEL&gt;'</span></span><br><span class="line"><span class="comment"> *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will</span></span><br><span class="line"><span class="comment"> *  turn off all logging for your tag. You can also create a local.prop file that with the</span></span><br><span class="line"><span class="comment"> *  following in it:</span></span><br><span class="line"><span class="comment"> *      'log.tag.&amp;lt;YOUR_LOG_TAG&gt;=&amp;lt;LEVEL&gt;'</span></span><br><span class="line"><span class="comment"> *  and place that in /data/local.prop.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> tag The tag to check.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> level The level to check.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> Whether or not that this is allowed to be logged.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IllegalArgumentException is thrown if the tag.length() &gt; 23</span></span><br><span class="line"><span class="comment"> *         for Nougat (7.0) releases (API &lt;= 23) and prior, there is no</span></span><br><span class="line"><span class="comment"> *         tag limit of concern after this API level.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">isLoggable</span><span class="params">(String tag, <span class="keyword">int</span> level)</span></span>;</span><br></pre></td></tr></table></figure></p><p> <strong>logger_entry_max_payload_native</strong>函数用于获取每条log的缓冲区大小，避免太长的log被截断而存入不完整.<br> <strong>isLoggable</strong>函数是在7.0之后的版本上加入的，可以获取到某个tag相对应的Level级别的log能否打印，可以通过在local.prop文件中设置这个属性.具体怎么设置，还需要继续往下追踪代码.</p><p> 接下来继续往下追踪println_native的流程,到frameworks/base/core/jni/android_util_Log.cpp<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * JNI registration.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> JNINativeMethod gMethods[] = &#123;</span><br><span class="line">    <span class="comment">/* name, signature, funcPtr */</span></span><br><span class="line">    &#123; <span class="string">"isLoggable"</span>,      <span class="string">"(Ljava/lang/String;I)Z"</span>, (<span class="keyword">void</span>*) android_util_Log_isLoggable &#125;,</span><br><span class="line">    &#123; <span class="string">"println_native"</span>,  <span class="string">"(IILjava/lang/String;Ljava/lang/String;)I"</span>, (<span class="keyword">void</span>*) android_util_Log_println_native &#125;,</span><br><span class="line">    &#123; <span class="string">"logger_entry_max_payload_native"</span>,  <span class="string">"()I"</span>, (<span class="keyword">void</span>*) android_util_Log_logger_entry_max_payload_native &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><p> JNI中，Log.java中的三个native方法，分别通过android_util_Log_isLoggable，android_util_Log_println_native，<br> android_util_Log_logger_entry_max_payload_native实现，我们先来看看frameworks/base/core/jni这个目录中Android这个编译文件，在这里我们看到需要的与log相关的依赖库有liblog,libnativehelper,libutils,libcutils,libandroidfw,libbase等这些lib库文件.</p><p> 一、查看android_util_Log_isLoggable函数的实现,最后调用的是__android_log_is_loggable函数,如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jboolean <span class="title">android_util_Log_isLoggable</span><span class="params">(JNIEnv* env, jobject clazz, jstring tag, jint level)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tag == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span>* chars = env-&gt;GetStringUTFChars(tag, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">if</span> (!chars) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    jboolean result = isLoggable(chars, level);</span><br><span class="line"></span><br><span class="line">    env-&gt;ReleaseStringUTFChars(tag, chars);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">static</span> jboolean <span class="title">isLoggable</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* tag, jint level)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> __android_log_is_loggable(level, tag, ANDROID_LOG_INFO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>  二、查看android_util_Log_println_native函数的实现，最后调用的是__android_log_buf_write函数，如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * In class android.util.Log:</span></span><br><span class="line"><span class="comment"> *  public static native int println_native(int buffer, int priority, String tag, String msg)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jint <span class="title">android_util_Log_println_native</span><span class="params">(JNIEnv* env, jobject clazz,</span></span></span><br><span class="line"><span class="function"><span class="params">        jint bufID, jint priority, jstring tagObj, jstring msgObj)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span>* tag = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span>* msg = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (msgObj == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        jniThrowNullPointerException(env, <span class="string">"println needs a message"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (bufID &lt; <span class="number">0</span> || bufID &gt;= LOG_ID_MAX) &#123;</span><br><span class="line">        jniThrowNullPointerException(env, <span class="string">"bad bufID"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (tagObj != <span class="literal">NULL</span>)</span><br><span class="line">        tag = env-&gt;GetStringUTFChars(tagObj, <span class="literal">NULL</span>);<span class="comment">//取出tag</span></span><br><span class="line">    msg = env-&gt;GetStringUTFChars(msgObj, <span class="literal">NULL</span>);<span class="comment">//取出msg</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);<span class="comment">//往缓冲区写</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (tag != <span class="literal">NULL</span>)</span><br><span class="line">        env-&gt;ReleaseStringUTFChars(tagObj, tag);</span><br><span class="line">    env-&gt;ReleaseStringUTFChars(msgObj, msg);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><a href="https://img-blog.csdn.net/20170103194253919" target="_blank" rel="noopener">log_JNI-liblog-lod流程图</a></p><p> 三、查看android_util_Log_logger_entry_max_payload_native函数，最后取的是LOGGER_ENTRY_MAX_PAYLOAD的值.</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * In class android.util.Log:</span></span><br><span class="line"><span class="comment"> *  private static native int logger_entry_max_payload_native()</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jint <span class="title">android_util_Log_logger_entry_max_payload_native</span><span class="params">(JNIEnv* env ATTRIBUTE_UNUSED,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                             jobject clazz ATTRIBUTE_UNUSED)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">static_cast</span>&lt;jint&gt;(LOGGER_ENTRY_MAX_PAYLOAD);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 本文主要研究log相关的，所以重点还是只研究liblog这个库文件，具体见下一节.</p><h1 id="运行时库层liblog模块log功能解析"><a href="#运行时库层liblog模块log功能解析" class="headerlink" title="运行时库层liblog模块log功能解析"></a>运行时库层liblog模块log功能解析</h1><p> <strong>liblog</strong>:system/core/liblog<br> 功能：Android Internal NDK logger interfaces  Android内部的NDK logger模块的接口.提供一些公共函数接口供其他模块调用,<br> 目前了解到的一下模块需要用到liblog库：<br> • logd<br> • logcat<br> • system/core/libvndksupport: VNDK -vendor NDK 各个芯片厂商自定义的NDK<br> • frameworks/native/services/audiomanager<br> 等等模块都需要用liblog库文件来处理log.</p><p> 阅读README文件.如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br></pre></td><td class="code"><pre><span class="line">DESCRIPTION</span><br><span class="line">       liblog  represents  an interface to the volatile Android Logging system</span><br><span class="line">       <span class="keyword">for</span> NDK (Native) applications  and  libraries.  Interfaces  <span class="keyword">for</span>  either</span><br><span class="line">       writing  or reading logs.  The <span class="built_in">log</span> buffers are divided up <span class="keyword">in</span> Main, Sys‐</span><br><span class="line">       tem, Radio and Events sub-logs.</span><br><span class="line">       liblog是Android Log系统用于NDK应用或者lib库的接口,此接口提供了读和写<span class="built_in">log</span>的方法,<span class="built_in">log</span></span><br><span class="line">       缓冲区被分为类Main.system.radio.event等几个部分.</span><br><span class="line"></span><br><span class="line">       The logging interfaces are a series of macros,  all  of  <span class="built_in">which</span>  can  be</span><br><span class="line">       overridden individually <span class="keyword">in</span> order to control the verbosity of the appli‐</span><br><span class="line">       cation or library.  [ASR]LOG[VDIWE] calls are used  to  <span class="built_in">log</span>  to  BAsic,</span><br><span class="line">       System or Radio sub-logs <span class="keyword">in</span> either the Verbose, Debug, Info, Warning or</span><br><span class="line">       Error priorities.  [ASR]LOG[VDIWE]_IF calls are used  to  perform  thus</span><br><span class="line">       based  on a condition being <span class="literal">true</span>.  IF_ALOG[VDIWE] calls are <span class="literal">true</span> <span class="keyword">if</span> the</span><br><span class="line">       current LOG_TAG is enabled at the specified priority.  LOG_ALWAYS_FATAL</span><br><span class="line">       is  used to ALOG a message, <span class="keyword">then</span> <span class="built_in">kill</span> the process.  LOG_FATAL call is a</span><br><span class="line">       variant of LOG_ALWAYS_FATAL,  only  enabled  <span class="keyword">in</span>  engineering,  and  not</span><br><span class="line">       release builds.  ALOG_ASSERT is used to ALOG a message <span class="keyword">if</span> the condition</span><br><span class="line">       is  <span class="literal">false</span>;   the   condition   is   part   of   the   logged   message.</span><br><span class="line">       LOG_EVENT_(INT|LONG)  is  used  to  drop binary content into the Events</span><br><span class="line">       sub-log.</span><br><span class="line">       译:<span class="built_in">log</span>接口就是一系列的宏,在每个应用或者库中为了更详细的控制<span class="built_in">log</span>的输出,在各个应用或者库中</span><br><span class="line">       可以对这些宏进行单独的覆盖处理.</span><br><span class="line">       [ASR]LOG[VDIWE] 类型：如ALOGV(format, ...).可以用于打印main,system,radio类</span><br><span class="line">       的各种级别的<span class="built_in">log</span>.</span><br><span class="line">       [ASR]LOG[VDIWE]_IF 类型：如ALOGV_IF(cond, format, ...).可以用于打印满足cond</span><br><span class="line">       条件的时候输出<span class="built_in">log</span>.</span><br><span class="line">       IF_ALOG[VDIWE] 类型:如IF_ALOGD().当当前的LOG_TAG在特殊优先级下运行使用时,可以正</span><br><span class="line">       常使用.</span><br><span class="line">       LOG_ALWAYS_FATAL 类型：如LOG_ALWAYS_FATAL(format, ...).用于打印一条<span class="built_in">kill</span>进程</span><br><span class="line">       的<span class="built_in">log</span>.</span><br><span class="line">       LOG_FATAL 类型：如LOG_FATAL(format, ...).与LOG_ALWAYS_FATAL一样的功能,不过它</span><br><span class="line">       只在Eng版本中打印,release版本中不会打印.</span><br><span class="line">       ALOG_ASSERT 类型:如ALOG_ASSERT(cond, format, ...).当cond不满足的时候打印.</span><br><span class="line">       LOG_EVENT_INT 类型：如LOG_EVENT_INT(tag,value).用于打印二进制内容到Eventlog中</span><br><span class="line"></span><br><span class="line">       The <span class="built_in">log</span> reading interfaces permit opening the  logs  either  singly  or</span><br><span class="line">       multiply,  retrieving  a  <span class="built_in">log</span>  entry  at  a  time <span class="keyword">in</span> time sorted order,</span><br><span class="line">       optionally limited to a specific pid and tail of the <span class="built_in">log</span>(s) and finally</span><br><span class="line">       a  call closing the logs.  A single <span class="built_in">log</span> can be opened with android_log‐</span><br><span class="line">       ger_list_open;  or  multiple  logs  can  be  opened  with  android_log‐</span><br><span class="line">       ger_list_alloc,  calling  <span class="keyword">in</span>  turn the android_logger_open <span class="keyword">for</span> each <span class="built_in">log</span></span><br><span class="line">       id.  Each entry can be retrieved  with  android_logger_list_read.   The</span><br><span class="line">       <span class="built_in">log</span>(s) can be closed with android_logger_list_free.  The logs should be</span><br><span class="line">       opened  with an  ANDROID_LOG_RDONLY  mode.   ANDROID_LOG_NONBLOCK  mode</span><br><span class="line">       will report when the  <span class="built_in">log</span> reading is <span class="keyword">done</span> with an  EAGAIN  error <span class="built_in">return</span></span><br><span class="line">       code,  otherwise the  android_logger_list_read  call will block <span class="keyword">for</span> new</span><br><span class="line">       entries.</span><br><span class="line">       译:<span class="built_in">log</span>的reading接口可以读取通过时间排序收到的一条<span class="built_in">log</span>.</span><br><span class="line">       单条<span class="built_in">log</span>可以通过 android_logger_list_open 函数打开,多条<span class="built_in">log</span>可以通过android_log‐</span><br><span class="line">       ger_list_alloc 调用 android_logger_list_open 打开.每条<span class="built_in">log</span>都可以被 android_l-</span><br><span class="line">       ogger_list_read 函数检索. 可以通过 android_logger_list_free 关闭回收. <span class="built_in">log</span>必须</span><br><span class="line">       在 ANDROID_LOG_RDONLY 的只读模式打开. 当<span class="built_in">log</span>读取完成后 ANDROID_LOG_NONBLOCK 模</span><br><span class="line">       式将会以 EAGAIN 形式返回,否则 android_logger_list_read 将会阻止读取新的<span class="built_in">log</span>.</span><br><span class="line"></span><br><span class="line">       The  ANDROID_LOG_WRAP  mode flag to the  android_logger_list_alloc_time</span><br><span class="line">       signals  logd to quiesce  the reader until the buffer is about to prune</span><br><span class="line">       at the start time <span class="keyword">then</span> proceed to dumping content.</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">       The  ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to</span><br><span class="line">       switch from the active logs to the persistent logs from before the last</span><br><span class="line">       reboot.</span><br><span class="line">       译:ANDROID_LOG_PSTORE 模式标识用于从上次重启之后使用 android_logger_open 打开</span><br><span class="line">       <span class="built_in">log</span>从 active logs 切换到 persistent logs.</span><br><span class="line"></span><br><span class="line">       The value returned by android_logger_open can be used as a parameter to</span><br><span class="line">       the  android_logger_clear  <span class="keyword">function</span> to empty the sub-log.  It is recom‐</span><br><span class="line">       mended to only open <span class="built_in">log</span> ANDROID_LOG_WRONLY <span class="keyword">in</span> that <span class="keyword">case</span>.</span><br><span class="line">       译:android_logger_open 函数的返回值可以作为 android_logger_clear 函数的参数来</span><br><span class="line">       清空打开的<span class="built_in">log</span>信息.仅推荐在 ANDROID_LOG_WRONLY 只写模式下打开<span class="built_in">log</span>时使用.</span><br><span class="line"></span><br><span class="line">       The value returned by android_logger_open can be used as a parameter to</span><br><span class="line">       the android_logger_get_log_(size|readable_size|version) to retrieve the</span><br><span class="line">       sub-log maximum size, readable size and <span class="built_in">log</span> buffer format protocol ver‐</span><br><span class="line">       sion  respectively.  android_logger_get_id returns the id that was used</span><br><span class="line">       when  opening  the  sub-log.    It  is  recommended  to  open  the  <span class="built_in">log</span></span><br><span class="line">       ANDROID_LOG_RDONLY <span class="keyword">in</span> these cases.</span><br><span class="line">       译:android_logger_open 函数的返回值可以作为 android_logger_get_log_ 函数的参</span><br><span class="line">       数.仅推荐在 ANDROID_LOG_RDONLY 只读模式下启动.</span><br><span class="line"></span><br><span class="line">       android_set_log_transport()  selects  transport  filters.  Argument  is</span><br><span class="line">       either LOGGER_DEFAULT, LOGGER_LOGD, LOGGER_NULL or LOGGER_LOCAL. Log to</span><br><span class="line">       logger daemon <span class="keyword">for</span> default or logd, drop contents on floor,  or <span class="built_in">log</span> into</span><br><span class="line">       <span class="built_in">local</span>   memory   respectively.       Both   android_set_log_transport()</span><br><span class="line">       and android_get_log_transport() <span class="built_in">return</span> the current  transport mask,  or</span><br><span class="line">       a negative errno <span class="keyword">for</span> any problems.</span><br><span class="line">       译:</span><br><span class="line"></span><br><span class="line">ERRORS</span><br><span class="line">       If messages fail, a negative error code will be returned to the <span class="built_in">caller</span>.</span><br><span class="line"></span><br><span class="line">       The -ENOTCONN <span class="built_in">return</span> code indicates that the logger daemon is stopped.</span><br><span class="line"></span><br><span class="line">       The  -EBADF <span class="built_in">return</span> code indicates that the <span class="built_in">log</span> access point can not be</span><br><span class="line">       opened, or the <span class="built_in">log</span> buffer id is out of range.</span><br><span class="line"></span><br><span class="line">       For the  -EAGAIN  <span class="built_in">return</span> code,  this means that the logging message was</span><br><span class="line">       temporarily backed-up either because of Denial Of Service (DOS) logging</span><br><span class="line">       pressure from some chatty application or service <span class="keyword">in</span> the Android system,</span><br><span class="line">       or <span class="keyword">if</span> too small of a value is <span class="built_in">set</span> <span class="keyword">in</span> /proc/sys/net/unix/max_dgram_qlen.</span><br><span class="line">       To aid <span class="keyword">in</span> diagnosing the occurence of this,  a binary event from liblog</span><br><span class="line">       will be sent to the  <span class="built_in">log</span>  daemon  once a  new  message  can get through</span><br><span class="line">       indicating how many  messages were  dropped  as a result.   Please take</span><br><span class="line">       action to resolve the structural problems at the <span class="built_in">source</span>.</span><br><span class="line"></span><br><span class="line">       It is generally not advised <span class="keyword">for</span> the <span class="built_in">caller</span> to retry the  -EAGAIN <span class="built_in">return</span></span><br><span class="line">       code as  this  will  only  make the  problem(s)  worse  and  cause your</span><br><span class="line">       application to temporarily drop to the  logger daemon  priority,  BATCH</span><br><span class="line">       scheduling policy and background task cgroup. If you require a group of</span><br><span class="line">       messages to be passed atomically,  merge  them  into  one  message with</span><br><span class="line">       embedded newlines to the maximum length LOGGER_ENTRY_MAX_PAYLOAD.</span><br><span class="line"></span><br><span class="line">       Other <span class="built_in">return</span> codes  from  writing operation can be returned.  Since the</span><br><span class="line">       library retries on EINTR, -EINTR should never be returned.</span><br></pre></td></tr></table></figure></p><p> 注意：下面这三个目录中的代码<br>    system/core/include/android<br>    system/core/include/log<br>    system/core/include/private<br>    和 liblog模块中下面三个目录下面的代码一模一样<br>    system/core/liblog/include/android<br>    system/core/liblog/include/log<br>    system/core/liblog/include/private<br>    同时也和system/core/liblog/include_vndk一模一样，<br>    怀疑这三个地方的文件是<strong>软连接</strong>.我们到system/core/include/android/log.h用ls -al查看，果然是软连接到了system/core/liblog/include/log/log.h文件.</p><p>   Q:为何要做这三个目录的软连接？</p><p> 我们从system/core/liblog/include/android/log.h里面看看关于liblog的一些说明.<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/******************************************************************</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * IMPORTANT NOTICE:</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   This file is part of Android's set of stable system headers</span></span><br><span class="line"><span class="comment"> *   exposed by the Android NDK (Native Development Kit) since</span></span><br><span class="line"><span class="comment"> *   platform release 1.5</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   Third-party source AND binary code relies on the definitions</span></span><br><span class="line"><span class="comment"> *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)</span></span><br><span class="line"><span class="comment"> *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS</span></span><br><span class="line"><span class="comment"> *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY</span></span><br><span class="line"><span class="comment"> *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Support routines to send messages to the Android in-kernel log buffer,</span></span><br><span class="line"><span class="comment"> * which can later be accessed through the 'logcat' utility.</span></span><br><span class="line"><span class="comment"> * //liblog支持往kernel的log缓冲区发送消息，这些消息可以通过logcat工具来进行读取.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Each log message must have</span></span><br><span class="line"><span class="comment"> *   - a priority</span></span><br><span class="line"><span class="comment"> *   - a log tag</span></span><br><span class="line"><span class="comment"> *   - some text</span></span><br><span class="line"><span class="comment"> * //每条log信息必须由以下三部分组成</span></span><br><span class="line"><span class="comment"> * // a)优先级</span></span><br><span class="line"><span class="comment"> * // b)tag 标签</span></span><br><span class="line"><span class="comment"> * // c)text 内容</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The tag normally corresponds to the component that emits the log message,</span></span><br><span class="line"><span class="comment"> * and should be reasonably small.</span></span><br><span class="line"><span class="comment"> * //Tag主要用于标识log发生的组件或者地方，不易过长.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Log message text may be truncated to less than an implementation-specific</span></span><br><span class="line"><span class="comment"> * limit (e.g. 1023 characters max).</span></span><br><span class="line"><span class="comment"> * //log消息文本可能会被截断为小于规定的长度,比如若规定最大字符串长度为1023个字节，超过的话，可能会被截断.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Note that a newline character ("\n") will be appended automatically to your</span></span><br><span class="line"><span class="comment"> * log message, if not already there. It is not possible to send several</span></span><br><span class="line"><span class="comment"> * messages and have them appear on a single line in logcat.</span></span><br><span class="line"><span class="comment"> * //如果没有换行符的话，会自动进行换行，不可能在单行中显示多条log信息.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * PLEASE USE LOGS WITH MODERATION://请尽量避免打log</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *  - Sending log messages eats CPU and slow down your application and the</span></span><br><span class="line"><span class="comment"> *    system.//发送log信息将消耗CPU而且会降低应用的性能.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *  - The circular log buffer is pretty small (&lt;64KB), sending many messages</span></span><br><span class="line"><span class="comment"> *    might push off other important log messages from the rest of the system.</span></span><br><span class="line"><span class="comment"> *    //log的缓冲区非常小，小于64KB，所以如果频繁发送太多的消息，可能会冲掉之前的重要消息.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *  - In release builds, only send log messages to account for exceptional</span></span><br><span class="line"><span class="comment"> *    conditions.//在release版本中，仅发送特殊限定的一些log</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">NOTE:</span> These functions MUST be implemented by /system/lib/liblog.so</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure></p><h1 id="logd模块功能解析"><a href="#logd模块功能解析" class="headerlink" title="logd模块功能解析"></a>logd模块功能解析</h1><p> logd 是Android L版本提出来的概念，其作用有：<br> 一：与liblog交互，保存Android运行期间的log.在Android L之前，log由kernel的ring buffer 保存，在Android L之后，log保存在用户空间。<br> 二：与logcat交互，接收从logcat传来的命令，将log从logd传给logcat.</p><p> logd代码位置：system/core/logd</p><p> logd模块相关的prop属性值<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></p><h2 id="logd-进程的启动"><a href="#logd-进程的启动" class="headerlink" title="logd 进程的启动"></a>logd 进程的启动</h2><p> 参考资料:<br> <a href="https://blog.csdn.net/yinjian1013/article/details/78261527" target="_blank" rel="noopener">android logd 原理及实现</a><br> <a href="https://blog.csdn.net/koffuxu/article/details/53893297" target="_blank" rel="noopener">Android L日志系统1——logd</a><br> <a href="https://zhuanlan.zhihu.com/p/24372024" target="_blank" rel="noopener">Android6.0 Log的工作机制</a></p><h1 id="Logcat源码解析"><a href="#Logcat源码解析" class="headerlink" title="Logcat源码解析"></a>Logcat源码解析</h1><p> logcat源码位于：system/core/logcat/目录<br> Logcat模块代码结构如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">logcat</span><br><span class="line">  └── inclide</span><br><span class="line">  |      └── <span class="built_in">log</span></span><br><span class="line">  |           └── getopt.h</span><br><span class="line">  |           └── log.h</span><br><span class="line">  └── Android.mk</span><br><span class="line">  └── event.logtags</span><br><span class="line">  └── getopt_long.cpp</span><br><span class="line">  └── logcat_main.cpp</span><br><span class="line">  └── logcat_system.cpp</span><br><span class="line">  └── logcat.cpp</span><br><span class="line">  └── logcatd_main.cpp</span><br><span class="line">  └── logcatd.rc</span><br><span class="line">  └── logpersist</span><br><span class="line">  └── .clang.format</span><br></pre></td></tr></table></figure></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">logcat</span><br><span class="line">├── Android.mk</span><br><span class="line">├── event.logtags</span><br><span class="line">├── getopt_long.cpp</span><br><span class="line">├── include</span><br><span class="line">│   └── <span class="built_in">log</span></span><br><span class="line">│       ├── getopt.h</span><br><span class="line">│       └── logcat.h</span><br><span class="line">├── logcat.cpp</span><br><span class="line">├── logcatd_main.cpp</span><br><span class="line">├── logcatd.rc</span><br><span class="line">├── logcat_main.cpp</span><br><span class="line">├── logcat_system.cpp</span><br><span class="line">├── logpersist</span><br><span class="line">├── MODULE_LICENSE_APACHE2</span><br><span class="line">├── NOTICE</span><br><span class="line">└── tests</span><br><span class="line">    ├── Android.mk</span><br><span class="line">    ├── exec_benchmark.cpp</span><br><span class="line">    ├── liblogcat_test.cpp</span><br><span class="line">    ├── logcat_benchmark.cpp</span><br><span class="line">    ├── logcatd_test.cpp</span><br><span class="line">    └── logcat_test.cpp</span><br></pre></td></tr></table></figure><p> Q: 为何可以用adb 后面带 logcat 的方式来抓取log？<br>  <a href="https://blog.csdn.net/u010223349/article/details/41120255" target="_blank" rel="noopener">ADB实现解析</a><br>  <a href="https://blog.csdn.net/ysh149216447/article/details/53334015" target="_blank" rel="noopener">Android adb实现原理</a></p><h2 id="模块文件分析"><a href="#模块文件分析" class="headerlink" title="模块文件分析"></a>模块文件分析</h2><p> 1) Android.mk<br> 从Android.mk文件来看，编译生成了logcat和logcatd两个可执行bin文件，一个liblogcat共享库文件，和一个logpersist.start文件<br> logcat 作用：  入口函数是logcat_main.cpp<br> logcatd 作用: 用于保存log的服务，入口函数是logcatd_main.cpp<br> liblogcat 作用: 给logcat test使用<br> logpersist.start : 依据logpersist会在system/bin目录，生成logpersist.start logpersist.stop logpersist.cat </p><p> 2)event.logtags<br> 定义了一些Event log</p><p> 3)getopt_long.cpp<br> 命令行解析函数，支持长选项解析，用于解析logcat命令的辅助工具.</p><p> 4)logcat_main.cpp</p><p> 参考资料<br> <a href="https://blog.csdn.net/luoshengyang/article/details/6606957" target="_blank" rel="noopener">Android日志系统Logcat源代码简要分析</a></p><h1 id="Logwrapper-模块解析"><a href="#Logwrapper-模块解析" class="headerlink" title="Logwrapper 模块解析"></a>Logwrapper 模块解析</h1><h1 id="Logger-驱动模块解析"><a href="#Logger-驱动模块解析" class="headerlink" title="Logger 驱动模块解析"></a>Logger 驱动模块解析</h1><p> 在用户态中的log是由logger驱动实现,使用logcat工具完成log的查看和收集.本节将关注Logger驱动的实现原理.<br> Logger驱动程序主要由两个文件构成，分别是：<br> kernel/drivers/staging/android/logger.c<br> kernel/drivers/staging/android/logger.h</p><h2 id="Logger系统数据结构"><a href="#Logger系统数据结构" class="headerlink" title="Logger系统数据结构"></a>Logger系统数据结构</h2><p> 先来看一下logger.h头文件的内容：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _LINUX_LOGGER_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _LINUX_LOGGER_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;linux/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;linux/ioctl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct user_logger_entry_compat - defines a single entry that is given to a logger</span></span><br><span class="line"><span class="comment"> * @len:The length of the payload  //有效负载长度</span></span><br><span class="line"><span class="comment"> * @__pad:Two bytes of padding that appear to be required //两个字节的空格</span></span><br><span class="line"><span class="comment"> * @pid:The generating process' process ID  //进程ID</span></span><br><span class="line"><span class="comment"> * @tid:The generating process' thread ID   //线程ID</span></span><br><span class="line"><span class="comment"> * @sec:The number of seconds that have elapsed since the Epoch  //秒数</span></span><br><span class="line"><span class="comment"> * @nsec:The number of nanoseconds that have elapsed since @sec   //纳秒数</span></span><br><span class="line"><span class="comment"> * @msg:The message that is to be logged  //log的内容</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The userspace structure for version 1 of the logger_entry ABI.</span></span><br><span class="line"><span class="comment"> * This structure is returned to userspace unless the caller requests</span></span><br><span class="line"><span class="comment"> * an upgrade to a newer ABI version.</span></span><br><span class="line"><span class="comment"> * 用户空间中一条log的结构</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">user_logger_entry_compat</span> &#123;</span></span><br><span class="line">__u16len;</span><br><span class="line">__u16__pad;</span><br><span class="line">__s32pid;</span><br><span class="line">__s32tid;</span><br><span class="line">__s32sec;</span><br><span class="line">__s32nsec;</span><br><span class="line"><span class="keyword">char</span>msg[<span class="number">0</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct logger_entry - defines a single entry that is given to a logger</span></span><br><span class="line"><span class="comment"> * @len:The length of the payload  //有效负载长度</span></span><br><span class="line"><span class="comment"> * @hdr_size:sizeof(struct logger_entry_v2) //</span></span><br><span class="line"><span class="comment"> * @pid:The generating process' process ID //进程ID</span></span><br><span class="line"><span class="comment"> * @tid:The generating process' thread ID // 线程ID</span></span><br><span class="line"><span class="comment"> * @sec:The number of seconds that have elapsed since the Epoch //秒</span></span><br><span class="line"><span class="comment"> * @nsec:The number of nanoseconds that have elapsed since @sec //纳秒</span></span><br><span class="line"><span class="comment"> * @euid:Effective UID of logger //User Identifier 用户ID</span></span><br><span class="line"><span class="comment"> * @msg:The message that is to be logged</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The structure for version 2 of the logger_entry ABI.</span></span><br><span class="line"><span class="comment"> * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)</span></span><br><span class="line"><span class="comment"> * is called with version &gt;= 2</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">logger_entry</span> &#123;</span></span><br><span class="line">__u16len;</span><br><span class="line">__u16hdr_size;</span><br><span class="line">__s32pid;</span><br><span class="line">__s32tid;</span><br><span class="line">__s32sec;</span><br><span class="line">__s32nsec;</span><br><span class="line"><span class="keyword">kuid_t</span>euid;</span><br><span class="line"><span class="keyword">char</span>msg[<span class="number">0</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_LOG_RADIO<span class="meta-string">"log_radio"</span><span class="comment">/* radio-related messages */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_LOG_EVENTS<span class="meta-string">"log_events"</span><span class="comment">/* system/hardware events */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_LOG_SYSTEM<span class="meta-string">"log_system"</span><span class="comment">/* system/framework messages */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_LOG_MAIN<span class="meta-string">"log_main"</span><span class="comment">/* everything else */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_ENTRY_MAX_PAYLOAD4076 <span class="comment">//每条日志记录最大长度4076字节</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __LOGGERIO0xAE</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_GET_LOG_BUF_SIZE_IO(__LOGGERIO, 1) <span class="comment">/* size of log */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_GET_LOG_LEN_IO(__LOGGERIO, 2) <span class="comment">/* used log len */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_GET_NEXT_ENTRY_LEN_IO(__LOGGERIO, 3) <span class="comment">/* next entry len */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_FLUSH_LOG_IO(__LOGGERIO, 4) <span class="comment">/* flush log */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_GET_VERSION_IO(__LOGGERIO, 5) <span class="comment">/* abi version */</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LOGGER_SET_VERSION_IO(__LOGGERIO, 6) <span class="comment">/* abi version */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">/* _LINUX_LOGGER_H */</span></span></span><br></pre></td></tr></table></figure></p><p> 我们看到有两个结构体 user_logger_entry_compat 和 logger_entry，均用来描述一条Log记录.</p><p> struct user_logger_entry_compat 是一个用于描述一条Log记录的结构体。len成员变量记录了这条记录的有效负载的长度，有效负载指定的日志记录本身的长度，但是不包括用于描述这个记录的struct logger_entry结构体。回忆一下我们调用android.util.Log接口来使用日志系统时，会指定日志的优先级别Priority、Tag字符串以及Msg字符串，Priority + Tag + Msg三者内容的长度加起来就是记录的有效负载长度了。__pad成员变量是用来对齐结构体的。pid和tid成员变量分别用来记录是哪条进程和线程写入了这条记录。sec和nsec成员变量记录日志写的时间。msg成员变量记录的就有效负载的内容了，它的大小由len成员变量来确定。</p><p> #define LOGGER_ENTRY_MAX_PAYLOAD    4076 表示每条日志记录最大长度4076字节=(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct logger_log - represents a specific log, such as 'main' or 'radio'</span></span><br><span class="line"><span class="comment"> * @buffer:The actual ring buffer</span></span><br><span class="line"><span class="comment"> * @misc:The "misc" device representing the log</span></span><br><span class="line"><span class="comment"> * @wq:The wait queue for @readers</span></span><br><span class="line"><span class="comment"> * @readers:This log's readers</span></span><br><span class="line"><span class="comment"> * @mutex:The mutex that protects the @buffer</span></span><br><span class="line"><span class="comment"> * @w_off:The current write head offset</span></span><br><span class="line"><span class="comment"> * @head:The head, or location that readers start reading at.</span></span><br><span class="line"><span class="comment"> * @size:The size of the log</span></span><br><span class="line"><span class="comment"> * @logs:The list of log channels</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This structure lives from module insertion until module removal, so it does</span></span><br><span class="line"><span class="comment"> * not need additional reference counting. The structure is protected by the</span></span><br><span class="line"><span class="comment"> * mutex 'mutex'.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">logger_log</span> &#123;</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span>*buffer;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">miscdevice</span><span class="title">misc</span>;</span></span><br><span class="line"><span class="keyword">wait_queue_head_t</span>wq;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list_head</span><span class="title">readers</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mutex</span><span class="title">mutex</span>;</span></span><br><span class="line"><span class="keyword">size_t</span>w_off;</span><br><span class="line"><span class="keyword">size_t</span>head;</span><br><span class="line"><span class="keyword">size_t</span>size;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list_head</span><span class="title">logs</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="title">LIST_HEAD</span><span class="params">(log_list)</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * struct logger_reader - a logging device open for reading</span></span><br><span class="line"><span class="comment"> * @log:The associated log</span></span><br><span class="line"><span class="comment"> * @list:The associated entry in @logger_log's list</span></span><br><span class="line"><span class="comment"> * @r_off:The current read head offset.</span></span><br><span class="line"><span class="comment"> * @r_all:Reader can read all entries</span></span><br><span class="line"><span class="comment"> * @r_ver:Reader ABI version</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This object lives from open to release, so we don't need additional</span></span><br><span class="line"><span class="comment"> * reference counting. The structure is protected by log-&gt;mutex.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">logger_reader</span> &#123;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">logger_log</span>*<span class="title">log</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list_head</span><span class="title">list</span>;</span></span><br><span class="line"><span class="keyword">size_t</span>r_off;</span><br><span class="line"><span class="keyword">bool</span>r_all;</span><br><span class="line"><span class="keyword">int</span>r_ver;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>   结构体struct logger_log就是真正用来保存日志的地方了。buffer成员变量变是用保存日志信息的内存缓冲区，它的大小由size成员变量确定。从misc成员变量可以看出，logger驱动程序使用的设备属于misc类型的设备，通过在Android模拟器上执行cat /proc/devices命令（可参考在Ubuntu上下载、编译和安装Android最新内核源代码（Linux Kernel）一文），可以看出，misc类型设备的主设备号是10。关于主设备号的相关知识，可以参考Android学习启动篇一文中提到的Linux Driver Development一书。wq成员变量是一个等待队列，用于保存正在等待读取日志的进程。readers成员变量用来保存当前正在读取日志的进程，正在读取日志的进程由结构体logger_reader来描述。mutex成员变量是一个互斥量，用来保护log的并发访问。可以看出，这里的日志系统的读写问题，其实是一个生产者-消费者的问题，因此，需要互斥量来保护log的并发访问。 w_off成员变量用来记录下一条日志应该从哪里开始写。head成员变量用来表示打开日志文件中，应该从哪一个位置开始读取日志</p><p>   结构体struct logger_reader用来表示一个读取日志的进程，log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者进程。r_off成员变量表示当前要读取的日志在缓冲区中的位置。</p><p>   struct logger_log结构体中用于保存日志信息的内存缓冲区buffer是一个循环使用的环形缓冲区，缓冲区中保存的内容是以struct logger_entry为单位的，每个单位的组成为：</p><pre><code>struct logger_entry | priority | tag | msg由于是内存缓冲区buffer是一个循环使用的环形缓冲区，给定一个偏移值，它在buffer中的位置由下logger_offset来确定：#define logger_offset(n)          ((n) &amp; (log-&gt;size - 1))</code></pre><h2 id="Logger模块的初始化过程分析"><a href="#Logger模块的初始化过程分析" class="headerlink" title="Logger模块的初始化过程分析"></a>Logger模块的初始化过程分析</h2><p> 继续看logger.c文件，定义了三个日志设备：<br> 待补充</p><h2 id="日志系统读取"><a href="#日志系统读取" class="headerlink" title="日志系统读取"></a>日志系统读取</h2><p> 待补充</p><h2 id="日志系统写入"><a href="#日志系统写入" class="headerlink" title="日志系统写入"></a>日志系统写入</h2><p> 待补充</p><h1 id="Eventlog解析"><a href="#Eventlog解析" class="headerlink" title="Eventlog解析"></a>Eventlog解析</h1><p> 在调试分析Android的过程中，比较常用的地查看EventLog，非常简洁明了地展现当前Activity各种状态,以及Window状态等，看完本节之后，如果在某个模块调试过程中，有几个比较关键的状态值需要关注，也可以自己定义一些Event事件，最后从eventlog中获取状态值.</p><p> 本节将从EventLog在代码中的使用开始解析EventLog的流程.</p><h2 id="Eventlog的使用"><a href="#Eventlog的使用" class="headerlink" title="Eventlog的使用"></a>Eventlog的使用</h2><p> 在代码中搜索*.logtags文件，结果如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">./system/bt/EventLogTags.logtags</span><br><span class="line">./system/core/logd/event.logtags</span><br><span class="line">./system/core/libsysutils/EventLogTags.logtags</span><br><span class="line">./system/core/liblog/event.logtags</span><br><span class="line">./system/core/logcat/event.logtags</span><br><span class="line">./system/core/storaged/EventLogTags.logtags</span><br><span class="line">./packages/apps/TimeZoneUpdater/src/main/com/android/timezone/updater/EventLogTags.logtags</span><br><span class="line">./packages/apps/QuickSearchBox/src/com/android/quicksearchbox/EventLogTags.logtags</span><br><span class="line">./packages/apps/Settings/src/com/android/settings/EventLogTags.logtags</span><br><span class="line">./packages/services/Telephony/src/com/android/phone/EventLogTags.logtags</span><br><span class="line">./packages/providers/CalendarProvider/src/com/android/providers/calendar/EventLogTags.logtags</span><br><span class="line">./packages/providers/ContactsProvider/src/com/android/providers/contacts/EventLogTags.logtags</span><br><span class="line">./frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags</span><br><span class="line">./frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/EventLogTags.logtags</span><br><span class="line">./frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags</span><br><span class="line">./frameworks/base/services/core/java/com/android/server/EventLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/android/content/EventLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/android/app/admin/SecurityLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/android/speech/tts/EventLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/android/net/EventLogTags.logtags</span><br><span class="line">./frameworks/base/core/java/android/webkit/EventLogTags.logtags</span><br><span class="line">./frameworks/<span class="keyword">native</span>/services/surfaceflinger/EventLog/EventLogTags.logtags</span><br><span class="line">./frameworks/ex/common/java/com/android/common/GoogleLogTags.logtags</span><br><span class="line">./frameworks/opt/telephony/src/java/com/android/internal/telephony/EventLogTags.logtags</span><br></pre></td></tr></table></figure></p><p> 从上面的结果可以看到，有很多的模块都有使用EventLog,在这里对于Java侧的SystemUI中EventLog为例进行剖析，对于C/C++层的EventLog留待后续分析.<br> 先来看frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags的部分代码：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># See system/core/logcat/event.logtags for a description of the format of this file.</span></span><br><span class="line"></span><br><span class="line">option java_package com.android.systemui;</span><br><span class="line"></span><br><span class="line"><span class="comment"># ---------------------------</span></span><br><span class="line"><span class="comment"># PhoneStatusBar.java</span></span><br><span class="line"><span class="comment"># ---------------------------</span></span><br><span class="line">36000 sysui_statusbar_touch (<span class="built_in">type</span>|1),(x|1),(y|1),(disable1|1),(disable2|1)</span><br><span class="line">36001 sysui_heads_up_status (key|3),(visible|1)</span><br><span class="line">36002 sysui_fullscreen_notification (key|3)</span><br><span class="line">36003 sysui_heads_up_escalation (key|3)</span><br><span class="line"><span class="comment"># sysui_status_bar_state: Logged whenever the status bar / keyguard state changes</span></span><br><span class="line"><span class="comment">## state: 0: SHADE, 1: KEYGUARD, 2: SHADE_LOCKED</span></span><br><span class="line"><span class="comment">## keyguardShowing: 1: Keyguard shown to the user (or keyguardOccluded)</span></span><br><span class="line"><span class="comment">## keyguardOccluded: 1: Keyguard active, but another activity is occluding it</span></span><br><span class="line"><span class="comment">## bouncerShowing: 1: Bouncer currently shown to the user</span></span><br><span class="line"><span class="comment">## secure: 1: The user has set up a secure unlock method (PIN, password, etc.)</span></span><br><span class="line"><span class="comment">## currentlyInsecure: 1: No secure unlock method set up (!secure), or trusted environment (TrustManager)</span></span><br><span class="line">36004 sysui_status_bar_state (state|1),(keyguardShowing|1),(keyguardOccluded|1),(bouncerShowing|1),(secure|1),(currentlyInsecure|1)</span><br></pre></td></tr></table></figure></p><p> 从第一行我们看到在system/core/logcat/event.logtags中有EventLog格式的描述，我们到system/core/logcat/event.logtags看看,event.logtags自身也是一个EventLog文件，但是在此文件的开头和结尾，有对logtags格式的文件格式进行了描述，如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># The entries in this file map a sparse set of log tag numbers to tag names.</span></span><br><span class="line"><span class="comment"># This is installed on the device, in /system/etc, and parsed by logcat.</span></span><br><span class="line"><span class="comment"># 此文件最终编译后位于system/etc/event-log-tags文件中，最终由logcat进行解析</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the </span></span><br><span class="line"><span class="comment"># negative values alone for now.)</span></span><br><span class="line"><span class="comment"># Tag编号为十进制,从0到2^31</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Tag names are one or more ASCII letters and numbers or underscores, i.e.</span></span><br><span class="line"><span class="comment"># "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former</span></span><br><span class="line"><span class="comment"># impacts log readability, the latter makes regex searches more annoying).</span></span><br><span class="line"><span class="comment"># Tag名字由字符串和数字组成,为了方便在log中搜索，name中避免使用空格和标点</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Tag numbers and names are separated by whitespace.  Blank lines and lines</span></span><br><span class="line"><span class="comment"># starting with '#' are ignored.</span></span><br><span class="line"><span class="comment"># Tag的编号和名字由一个空格隔开,空行或者以#开头的行将被忽略</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Optionally, after the tag names can be put a description for the value(s)</span></span><br><span class="line"><span class="comment"># of the tag. Description are in the format</span></span><br><span class="line"><span class="comment">#    (&lt;name&gt;|data type[|data unit])</span></span><br><span class="line"><span class="comment"># Multiple values are separated by commas.</span></span><br><span class="line"><span class="comment"># 根据需要,tag names后面可以加上这个tag的values来描述这个tag的打印格式。</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># 数据类型由下列的数字组成:(如果数字类型时int,则data type用1表示)</span></span><br><span class="line"><span class="comment"># The data type is a number from the following values:</span></span><br><span class="line"><span class="comment"># 1: int</span></span><br><span class="line"><span class="comment"># 2: long</span></span><br><span class="line"><span class="comment"># 3: string</span></span><br><span class="line"><span class="comment"># 4: list</span></span><br><span class="line"><span class="comment"># 5: float</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># data unit 表示数据格式，相当于data的单位：(1表示Number of objects)</span></span><br><span class="line"><span class="comment"># The data unit is a number taken from the following list:</span></span><br><span class="line"><span class="comment"># 1: Number of objects 对象个数</span></span><br><span class="line"><span class="comment"># 2: Number of bytes 对象字节数</span></span><br><span class="line"><span class="comment"># 3: Number of milliseconds 毫秒</span></span><br><span class="line"><span class="comment"># 4: Number of allocations 分配的个数</span></span><br><span class="line"><span class="comment"># 5: Id</span></span><br><span class="line"><span class="comment"># 6: Percent</span></span><br><span class="line"><span class="comment"># s: Number of seconds (monotonic time)</span></span><br><span class="line"><span class="comment"># Default value for data of type int/long is 2 (bytes).</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># <span class="doctag">TODO:</span> generate ".java" and ".h" files with integer constants from this file.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># NOTE - the range 1000000-2000000 is reserved for partners and others who</span></span><br><span class="line"><span class="comment"># want to define their own log tags without conflicting with the core platform.</span></span><br><span class="line"><span class="comment"># 1000000-2000000的tag number是留给合作厂商或者其他开发者用户扩展用的。</span></span><br></pre></td></tr></table></figure></p><p> 根据system/core/logcat/event.logtags中对logtags格式文件的描述，我们取上面的SystemUI中的logtags文件中的一个Event事件为例来说明：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">36000</span> sysui_statusbar_touch (type|<span class="number">1</span>),(x|<span class="number">1</span>),(y|<span class="number">1</span>),(disable1|<span class="number">1</span>),(disable2|<span class="number">1</span>)</span><br></pre></td></tr></table></figure></p><p> 对应定义<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#    (&lt;name&gt;|data type[|data unit])</span><br></pre></td></tr></table></figure></p><p> 则：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Tag Number: <span class="number">36000</span></span><br><span class="line">Tag name: sysui_statusbar_touch</span><br><span class="line">value <span class="number">1</span>: name=<span class="string">"type"</span>, data_type=<span class="number">1</span>-&gt;<span class="keyword">int</span></span><br><span class="line">value <span class="number">2</span>: name=<span class="string">"x"</span>, data_type=<span class="number">1</span>-&gt;<span class="keyword">int</span></span><br><span class="line">value <span class="number">3</span>: name=<span class="string">"y"</span>, data_type=<span class="number">1</span>-&gt;<span class="keyword">int</span></span><br><span class="line">value <span class="number">4</span>: name=<span class="string">"disable1"</span>, data_type=<span class="number">1</span>-&gt;<span class="keyword">int</span></span><br><span class="line">value <span class="number">5</span>: name=<span class="string">"disable2"</span>, data_type=<span class="number">1</span>-&gt;<span class="keyword">int</span></span><br></pre></td></tr></table></figure></p><p> 接下来看看如何在SystemUI的Java代码中使用,在<br> frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java中<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">interceptTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (DEBUG_GESTURES) &#123;</span><br><span class="line">        <span class="keyword">if</span> (event.getActionMasked() != MotionEvent.ACTION_MOVE) &#123;</span><br><span class="line">            EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,</span><br><span class="line">                    event.getActionMasked(), (<span class="keyword">int</span>) event.getX(), (<span class="keyword">int</span>) event.getY(),</span><br><span class="line">                    mDisabled1, mDisabled2);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure></p><p> 可以看到在StatusBar的interceptTouchEvent方法中，将touch事件写入了event log中.</p><h2 id="相关功能模块代码"><a href="#相关功能模块代码" class="headerlink" title="相关功能模块代码"></a>相关功能模块代码</h2><p> 本节将从EventLog调用流程来分析相关的关键代码块：</p><h3 id="framework模块："><a href="#framework模块：" class="headerlink" title="framework模块："></a>framework模块：</h3><p> • frameworks/base/core/java/android/util/EventLogTags.java //已经被弃用，8.1.0中使用的是EventLog.java<br> • frameworks/base/core/java/android/util/EventLog.java<br>  主要在EventLog.java文件中实现了下列函数供Java侧的各个模块调用：<br>  a)实现writeEvent函数，通过JNI，调用到底层的代码，最终将event写入到/dev/log/events，调用关系如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">EventLog.writeEvent() [android.util.EventLog][EventLog.java]</span><br><span class="line">android_util_EventLog_writeEvent_Integer() [libandroid_runtime.so][android_util_EventLog.cpp]</span><br><span class="line">android_bWriteLog() -&gt; __android_log_bwrite() [liblog.so][]</span><br><span class="line">write_to_log(LOG_ID_EVENTS, vec, <span class="number">2</span>) -&gt; __write_to_log_kernel() [liblog.so]</span><br></pre></td></tr></table></figure></p><p> b)实现readEvents函数，通过JNI,调用到底层代码, 具体使用场景还不没找,提供了方法来读取对应type的eventlog<br> c)实现getTagName函数，通过给出的ID，返回Tag的名称，主要通过在system/etc/event-log-tags文件里面进行查找实现，供Java侧其他模块调用<br> d)实现getTagCode函数，通过给出的Tag名称，返回Tag的ID，主要通过在system/etc/event-log-tags文件里面进行查找实现，供Java侧其他模块调用<br> e)实现内部静态类Event，这个是8.1.0之前的老版本的EventLog的实现.暂不关注.</p><h3 id="JNI-模块："><a href="#JNI-模块：" class="headerlink" title="JNI 模块："></a>JNI 模块：</h3><p> • frameworks/base/core/jni/android_util_EventLog.cpp<br> 实现了EventLog.java中的writeEvent和readEvent函数.在writeEvent函数中调用android_log_event_list类的write方法.<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * In class android.util.EventLog:</span></span><br><span class="line"><span class="comment"> *  static native int writeEvent(int tag, int value)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jint <span class="title">android_util_EventLog_writeEvent_Integer</span><span class="params">(JNIEnv* env UNUSED,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     jobject clazz UNUSED,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     jint tag, jint value)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">android_log_event_list <span class="title">ctx</span><span class="params">(tag)</span></span>;</span><br><span class="line">    ctx &lt;&lt; (<span class="keyword">int32_t</span>)value;</span><br><span class="line">    <span class="keyword">return</span> ctx.write();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> framework模块的代码还有:<br> frameworks/base/core/java/com/android/internal/logging/MetricsLogger.java<br> frameworks/base/core/java/android/metrics/MetricsReader.java<br> 以sysui_multi_action事件分析为例</p><h3 id="liblog模块："><a href="#liblog模块：" class="headerlink" title="liblog模块："></a>liblog模块：</h3><p> • system/core/liblog/include/log/log_event_list.h //定义了android_log_event_list类<br> • system/core/liblog/log_event_list.c //实现了android_log_event_list类的相关方法<br> • system/core/liblog/event_tag_map.cpp<br> • system/core/liblog/logger_write.c<br> liblog模块,在log_event_list.h中android_log_event_list.h中，初始化后，被&lt;&lt;赋值，执行android_log_write_int32函数，android_log_write_int32的实现在log_event_list.c，接着执行ctx.write().<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">write</span><span class="params">(<span class="keyword">log_id_t</span> id = LOG_ID_EVENTS)</span> </span>&#123;</span><br><span class="line">  <span class="comment">/* facilitate -EBUSY retry */</span></span><br><span class="line">  <span class="keyword">if</span> ((ret == -EBUSY) || (ret &gt; <span class="number">0</span>)) ret = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">int</span> retval = android_log_write_list(ctx, id);</span><br><span class="line">  <span class="comment">/* existing errors trump transmission errors */</span></span><br><span class="line">  <span class="keyword">if</span> (!ret) ret = retval;</span><br><span class="line">  <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 在log_event_list.c 中执行android_log_write_list函数，如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Logs the list of elements to the event log.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function">LIBLOG_ABI_PUBLIC <span class="keyword">int</span> <span class="title">android_log_write_list</span><span class="params">(android_log_context ctx,</span></span></span><br><span class="line"><span class="function"><span class="params">                                             <span class="keyword">log_id_t</span> id)</span> </span>&#123;</span><br><span class="line">  android_log_context_internal* context;</span><br><span class="line">  <span class="keyword">const</span> <span class="keyword">char</span>* msg;</span><br><span class="line">  <span class="keyword">ssize_t</span> len;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ((id != LOG_ID_EVENTS) &amp;&amp; (id != LOG_ID_SECURITY)) &#123;</span><br><span class="line">    <span class="keyword">return</span> -EINVAL;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  context = (android_log_context_internal*)ctx;</span><br><span class="line">  <span class="keyword">if</span> (!context || (kAndroidLoggerWrite != context-&gt;read_write_flag)) &#123;</span><br><span class="line">    <span class="keyword">return</span> -EBADF;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (context-&gt;list_nest_depth) &#123;</span><br><span class="line">    <span class="keyword">return</span> -EIO;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/* NB: if there was overflow, then log is truncated. Nothing reported */</span></span><br><span class="line">  context-&gt;storage[<span class="number">1</span>] = context-&gt;count[<span class="number">0</span>];</span><br><span class="line">  len = context-&gt;len = context-&gt;pos;</span><br><span class="line">  msg = (<span class="keyword">const</span> <span class="keyword">char</span>*)context-&gt;storage;</span><br><span class="line">  <span class="comment">/* it's not a list */</span></span><br><span class="line">  <span class="keyword">if</span> (context-&gt;count[<span class="number">0</span>] &lt;= <span class="number">1</span>) &#123;</span><br><span class="line">    len -= <span class="keyword">sizeof</span>(<span class="keyword">uint8_t</span>) + <span class="keyword">sizeof</span>(<span class="keyword">uint8_t</span>);</span><br><span class="line">    <span class="keyword">if</span> (len &lt; <span class="number">0</span>) &#123;</span><br><span class="line">      len = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    msg += <span class="keyword">sizeof</span>(<span class="keyword">uint8_t</span>) + <span class="keyword">sizeof</span>(<span class="keyword">uint8_t</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (id == LOG_ID_EVENTS)</span><br><span class="line">             ? __android_log_bwrite(context-&gt;tag, msg, len)</span><br><span class="line">             : __android_log_security_bwrite(context-&gt;tag, msg, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 最后执行__android_log_bwrite函数，在logger_write.c中__android_log_bwrite函数如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">LIBLOG_ABI_PUBLIC <span class="keyword">int</span> __android_log_bwrite(<span class="keyword">int32_t</span> tag, <span class="keyword">const</span> <span class="keyword">void</span>* payload,</span><br><span class="line">                                           <span class="keyword">size_t</span> len) &#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">iovec</span> <span class="title">vec</span>[2];</span></span><br><span class="line"></span><br><span class="line">  vec[<span class="number">0</span>].iov_base = &amp;tag;</span><br><span class="line">  vec[<span class="number">0</span>].iov_len = <span class="keyword">sizeof</span>(tag);</span><br><span class="line">  vec[<span class="number">1</span>].iov_base = (<span class="keyword">void</span>*)payload;</span><br><span class="line">  vec[<span class="number">1</span>].iov_len = len;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> write_to_log(LOG_ID_EVENTS, vec, <span class="number">2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> write_to_log将此event事件写入dev/log/event节点中.<br> 可以继续追踪write_to_log函数,在logger_write.c中<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> __write_to_log_init(<span class="keyword">log_id_t</span>, struct iovec* vec, <span class="keyword">size_t</span> nr);</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="title">int</span> <span class="params">(*write_to_log)</span><span class="params">(<span class="keyword">log_id_t</span>, struct iovec* vec,</span></span></span><br><span class="line"><span class="function"><span class="params">                           <span class="keyword">size_t</span> nr)</span> </span>= __write_to_log_init;</span><br></pre></td></tr></table></figure></p><p> 则看__write_to_log_init函数，如下：<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> __write_to_log_init(<span class="keyword">log_id_t</span> log_id, struct iovec* vec, <span class="keyword">size_t</span> nr) &#123;</span><br><span class="line">  __android_log_lock();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (write_to_log == __write_to_log_init) &#123;</span><br><span class="line">    <span class="keyword">int</span> ret;</span><br><span class="line"></span><br><span class="line">    ret = __write_to_log_initialize();</span><br><span class="line">    <span class="keyword">if</span> (ret &lt; <span class="number">0</span>) &#123;</span><br><span class="line">      __android_log_unlock();</span><br><span class="line">      <span class="keyword">if</span> (!list_empty(&amp;__android_log_persist_write)) &#123;</span><br><span class="line">        __write_to_log_daemon(log_id, vec, nr);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    write_to_log = __write_to_log_daemon;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  __android_log_unlock();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> write_to_log(log_id, vec, nr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 追的有点远了，打住，回到eventlog的正题.</p><p> 从上面的流程来看，在Java代码中相应函数内对EventLog进行打点,最后通过JNI回调和传参，就可以将Eventlog写入/dev/log/event节点了.//等等，8.1.0好像不是写入这个节点…<br> 在这个过程中，我们用到了一类文件<em>.logtags，从最还是我们看到了，这类文件主要用来用固定的格式来定义个EventLog事件，然后在Java文件中我们就可以直接使用定义好的事件名称和函数个数来打点Event事件.至于Java侧如何能够成功的调用Eventlog，大体流程是，Android代码在编译的过程中，build模块的一些脚本会自动的将相应模块的</em>.logtags文件中的Event事件收集汇总到system/etc/event-log-tags文件中，同时也会将对应的*.logtags文件转换成一个EventLogTags.java文件看下面的分析</p><h3 id="system中logd模块："><a href="#system中logd模块：" class="headerlink" title="system中logd模块："></a>system中logd模块：</h3><p> • system/core/logd/LogTags.cpp<br> • system/core/logd/LogTags.h<br>  读取system/etc/event-log-tags文件</p><h3 id="build模块："><a href="#build模块：" class="headerlink" title="build模块："></a>build模块：</h3><p> • build/tools/java-event-log-tags.py //将对应的*.logtags文件转换为java文件<br> • build/tools/event_log_tags.py   //输出event-log-tags文件<br> • build/tools/merge-event-log-tags.py //将生成的多个event-log-tags文件，整合到一个event-log-tags文件中<br>  build模块中的脚本，从代码来看干了两件事情：<br>  a)负责将各个模块中的EventLogTags.logtags文件转化为相对应的java文件，并输出到out/target/common/obj/JAVA_LIBRARIES 对应的目录中.<br>  b)负责将各个模块中的*.logtags文件中的事件，进行收集，并输出到out/…/system/etc/event-log-tags文件中</p><p>  对于生成EventLogTags.java文件，我们还是以上面的SystemUI模块为例，SystemUI模块中有一个EventLogTags.logtags文件<br>  frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags，我们在单编完SystemUI之后，到out/target/common/obj/JAVA_LIBRARIES目录查找systemui相关的lib库文件,找到<br>  out/target/common/obj/JAVA_LIBRARIES/SystemUI-tags_intermediates/src/src/com/android/systemui/EventLogTags.java<br>  下面我们从此文件中抽出我们上面以SystemUI为例时的sysui_statusbar_touch事件在此java文件中的结果如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* This file is auto-generated.  DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> * Source file: frameworks/base/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> com.android.systemui;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EventLogTags</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="title">EventLogTags</span><span class="params">()</span> </span>&#123; &#125;  <span class="comment">// don't instantiate</span></span><br><span class="line">  ...</span><br><span class="line">  <span class="comment">/** 36010 sysui_panelbar_touch (type|1),(x|1),(y|1),(enabled|1) */</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SYSUI_PANELBAR_TOUCH = <span class="number">36010</span>;</span><br><span class="line">  ...</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">writeSysuiPanelbarTouch</span><span class="params">(<span class="keyword">int</span> type, <span class="keyword">int</span> x, <span class="keyword">int</span> y, <span class="keyword">int</span> enabled)</span> </span>&#123;</span><br><span class="line">    android.util.EventLog.writeEvent(SYSUI_PANELBAR_TOUCH, type, x, y, enabled);</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br></pre></td></tr></table></figure></p><p> 可以看出多了一个静态常量和一个API可以调用，所以在源码里面可以这样调用（android源码的做法）：<br>EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,<br>                            sr.crashCount, sr.shortName, app.pid);<br>这里只是用了AM_SERVICE_CRASHED_TOO_MUCH这个常量而没有调专用API，或者这样用（测试可以编译通过）：<br>EventLog.writeAmServiceCrashedTooMuch(sr.crashCount, sr.shortName, app.pid);</p><h3 id="EventLog在解Bug中的应用"><a href="#EventLog在解Bug中的应用" class="headerlink" title="EventLog在解Bug中的应用"></a>EventLog在解Bug中的应用</h3><p> 下面列举tag可能使用的部分场景：<br>    am_low_memory：位于AMS.killAllBackgroundProcesses或者AMS.appDiedLocked，记录当前Lru进程队列长度。<br>    am_pss：位于AMS.recordPssSampleLocked(<br>    am_meminfo：位于AMS.dumpApplicationMemoryUsage<br>    am_proc_start:位于AMS.startProcessLocked，启动进程<br>    am_proc_bound:位于AMS.attachApplicationLocked<br>    am_kill: 位于ProcessRecord.kill，杀掉进程<br>    am_anr: 位于AMS.appNotResponding<br>    am_crash:位于AMS.handleApplicationCrashInner<br>    am_wtf:位于AMS.handleApplicationWtf<br>    am_activity_launch_time：位于ActivityRecord.reportLaunchTimeLocked()，后面两个参数分别是thisTime和 totalTime.<br>    am_activity_fully_drawn_time:位于ActivityRecord.reportFullyDrawnLocked, 后面两个参数分别是thisTime和 totalTime<br>    am_broadcast_discard_filter:位于BroadcastQueue.logBroadcastReceiverDiscardLocked<br>    am_broadcast_discard_app:位于BroadcastQueue.logBroadcastReceiverDiscardLocked</p><p>Activity生命周期相关的方法:<br>    am_on_resume_called: 位于AT.performResumeActivity<br>    am_on_paused_called: 位于AT.performPauseActivity, performDestroyActivity<br>    am_resume_activity: 位于AS.resumeTopActivityInnerLocked<br>    am_pause_activity: 位于AS.startPausingLocked<br>    am_finish_activity: 位于AS.finishActivityLocked, removeHistoryRecordsForAppLocked<br>    am_destroy_activity: 位于AS.destroyActivityLocked<br>    am_focused_activity: 位于AMS.setFocusedActivityLocked, clearFocusedActivity<br>    am_restart_activity: 位于ASS.realStartActivityLocked<br>    am_create_activity: 位于ASS.startActivityUncheckedLocked<br>    am_new_intent: 位于ASS.startActivityUncheckedLocked<br>    am_task_to_front: 位于AS.moveTaskToFrontLocked</p><p>下面列举tag可能使用的部分场景：<br>    power_sleep_requested: 位于PMS.goToSleepNoUpdateLocked<br>    power_screen_state:位于Notifer.handleEarlyInteractiveChange, handleLateInteractiveChange</p><p>   battery_level: [19,3660,352] //剩余电量19%, 电池电压3.66v, 电池温度35.2℃<br>   power_screen_state: [0,3,0,0] // 灭屏状态(0), 屏幕超时(3). 当然还有其他设备管理策略(1),其他理由都为用户行为(2)<br>   power_screen_state: [1,0,0,0] // 亮屏状态(1)</p><p>补充：<br>am_proc_start (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3)<br>am_proc_start:[0,9227,10002,com.android.browser,contentprovider,com.android.browser/.provider.BrowserProvider2]<br>–&gt; (User|1|5) ==&gt; 名字为User, 数据类型为1，数据单位为5）<br>数据类型：1: int、2: long、3: string、4: list<br>数据单位：1: Number of objects(对象个数)、2: Number of bytes(字节数)、3: Number of milliseconds(毫秒)、4: Number of allocations(分配个数)、5: Id、6: Percent(百分比)<br>–&gt; 进程启动: UserId=0, pid=9227, uid=10002, ProcessName=com.android.browser, 数据类型=ContentProvider, 组件=com.android.browser/.provider.BrowserProvider2</p><h3 id="event-log使用小技巧"><a href="#event-log使用小技巧" class="headerlink" title="event log使用小技巧"></a>event log使用小技巧</h3><p>• 通过Tag ID来过滤event log<br> adb logcat -t 524292 -b events //打印出所有sysui_multi_action事件的event log<br> adb logcat -v threadtime -t 524292 -b events<br>• 通过Tag name来过滤event log<br> adb logcat -b events | grep -rn sysui_multi_action //打印出所有sysui_multi_action事件的event log<br> adb logcat -v threadtime -b events | grep -rn sysui_multi_action</p><p><strong><a href="https://github.com/EricChows/logcat/blob/master/8.1.0-event-log/system/etc/event-log-tags" target="_blank" rel="noopener">在Android中所有已经定义了的Eventlog事件可以在这里查看</a></strong><br> <a href="https://github.com/EricChows/logcat/tree/master/8.1.0-event-log" target="_blank" rel="noopener">Eventlog相关的文件可以到这里查看</a></p><p> 如下待完成补充：<br> C/C++模块中EventLog的使用，<br> • system/core/logcat/event.logtags</p><p> 思考：Event log的优势在哪里？其实Event log要实现的功能，main log其实也是可以实现的.<br> 我的回答：对不同log划分了不同缓冲区后，就不用担心log被覆盖掉，关键的event log不用担心main太多被冲掉.</p><p>参考阅读：<br><a href="https://blog.csdn.net/yaowei514473839/article/details/53513435" target="_blank" rel="noopener">Android event日志打印原理</a><br><a href="https://blog.csdn.net/darkengine/article/details/8477502" target="_blank" rel="noopener">【framework】EventLog分析</a></p><h1 id="Android中其他log分析"><a href="#Android中其他log分析" class="headerlink" title="Android中其他log分析"></a>Android中其他log分析</h1><h1 id="LINUX-KERNEL源代码目录结构"><a href="#LINUX-KERNEL源代码目录结构" class="headerlink" title="LINUX KERNEL源代码目录结构"></a>LINUX KERNEL源代码目录结构</h1><p>/arch：硬件体系结构相关的代码，每种平台占一个相应的目录<br>/drivers：设备驱动程序，每个不同的驱动占用一个子目录<br>/fs：文件系统，包含所有的文件系统代码和各种类型的文件操作代码，它的每一个子目录支持一个文件系统<br>/include：包括编译核心所需要的大部分头文件。与平台无关的头文件在 include/linux 子目录下，与intel cpu 相关的头文件在 include/asm-i386<br>          子目录下，而 include/scsi 目录则是有关scsi 设备的头文件目录<br>init：这个目录包含核心的初始化代码（不是系统的引导代码），包含两个文件 main.c 和 Version.c，这是研究核心如何工作的好起点之一<br>/ipc：核心的进程间通讯的代码<br>/kernel：主要的核心代码，此目录下的文件是内核的最核心部分，包括进程调度、定时器等，实现了大多数Linux 系统的内核函数，其中重要的<br>         文件尾 sched.c ；同样，和体系结构相关的代码在 arch/*/kernel 中<br>/lib：放置核心库代码<br>/mm：这个目录包括所有独立于 CPU 体系机构的内存管理代码，如页式存储管理内存的分配和释放等；而合体系结构相关的内存管理代码则<br>     位于 arch/*/mm<br>/net：网络相关代码，实现了各种常见的网络协议<br>/scripts：描述文件，脚本，用于对核心的配置<br>/security：主要是一个SELinux 的模块<br>/sound：常用音频设备的驱动程序等<br>/usr：实现了一个 cpio<br>/block：部分块设备驱动程序<br>/crypto：常用加密和散列算法（如AES、SHA等），以及一些压缩和CRC 校验算法<br>/Documentation：一套关于内核部分的调用解释和注释用的英文文档</p><p>根目录下几个文件：<br>COPYING：GPL 版权申明，对具有GPL 版权的源代码改动而形成的程序，具有使用GPL 发表的义务，如公开源代码<br>CREDITS：光荣榜。对Linux 做出过很大贡献的一些人的信息<br>Kbuild：是编译内核的软件环境，它泛指构建一个完全并能够运行Linux 内核所需要的一切资源。这些资源包含构建程序、脚本、中间件、配置<br>        文件和Makefile<br>MAINTAINERS：维护人员列表，对当前版本的内核部分都有谁负责<br>Makefile：第一个Makefile 文件。用来组织内核的各模块，记录了各模块相互之间的联系和依托关系，编译时使用；仔细阅读各子目录下的<br>          Makefile文件对弄清各个文件之间的联系和依托关系很有帮助<br>ReadMe：核心以及编译配置方法简单介绍取模<br>REPORING-BUGS：有关报告 Bug 的一些内容</p><p>Android 解读main log和event log日志信息<br><a href="https://blog.csdn.net/yelangjueqi/article/details/52621903" target="_blank" rel="noopener">https://blog.csdn.net/yelangjueqi/article/details/52621903</a></p><p>Log中’main’, ‘system’, ‘radio’, ‘events’以及android log分析<br><a href="https://www.cnblogs.com/zhengtu2015/p/5134012.html" target="_blank" rel="noopener">https://www.cnblogs.com/zhengtu2015/p/5134012.html</a></p><p>关于Android Log的一些思考<br><a href="https://droidyue.com/blog/2015/11/01/thinking-about-android-log/" target="_blank" rel="noopener">https://droidyue.com/blog/2015/11/01/thinking-about-android-log/</a></p><p><a href="https://blog.csdn.net/fishmai/article/details/52398537" target="_blank" rel="noopener">https://blog.csdn.net/fishmai/article/details/52398537</a></p><p>crash/WTF/ANR/Low on memory  —dropbox</p><p>手机的默认的日志目录：</p><p> /data/local/tmp/*</p><p>/data/tmp/*</p><p>/data/system/usagestats/*</p><p>/data/system/appusagestates/*</p><p>/data/system/dropbox/*</p><p>/data/tombstones/*</p><p>/data/anr/*</p><p>logcat的日志在</p><p>/dev/log/main</p><p>有/data/local/log目录的，可以保存3-4天的log。</p><p> 需要对Android的log机制有深入的研究</p><p> logd源码分析</p><p>logd源码位置:<a href="">system/core/logd</a></p><p>  logwraper 源码分析<br>logwraper源码位置：<a href="">system/core/logwrapper/logwrap.c</a></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6581828" target="_blank" rel="noopener">浅谈Android系统开发中LOG的使用</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6595744" target="_blank" rel="noopener">Android日志系统驱动程序Logger源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6598703" target="_blank" rel="noopener">Android应用程序框架层和系统运行库层日志系统源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6606957" target="_blank" rel="noopener">Android日志系统Logcat源代码简要分析</a></p><p><a href="https://blog.csdn.net/armwind/article/details/52166139" target="_blank" rel="noopener">misc设备</a> 杂项设备<br>[mutex互斥锁]<br><a href="https://blog.csdn.net/koffuxu/article/details/53997348" target="_blank" rel="noopener">Android L日志系统2——JavaAPI与liblog</a><br><a href="https://www.cnblogs.com/palance/p/5247093.html" target="_blank" rel="noopener">Android6的Logger日志系统</a><br><a href="https://zhuanlan.zhihu.com/p/24372024" target="_blank" rel="noopener">Android6.0 Log的工作机制</a><br><a href="https://blog.csdn.net/shusuanly/article/details/80577306" target="_blank" rel="noopener">Android O 配置logcatd日志系统</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;LOG是广泛使用的用来记录程序执行过程的机制，它既可以用于程序调试，也可以用于产品运营中的事件记录。在Android系统中，提供了简单、便利
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android Log" scheme="http://yoursite.com/tags/Android-Log/"/>
    
  </entry>
  
  <entry>
    <title>Android Launcher 启动Activity工作流程</title>
    <link href="http://yoursite.com/Android-Launcher-Activity/"/>
    <id>http://yoursite.com/Android-Launcher-Activity/</id>
    <published>2018-02-25T07:45:27.000Z</published>
    <updated>2018-08-07T06:57:46.212Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>通过<a href="../Android-Binder-Analysis">Binder之应用层总结与分析</a>可以了解到进程间通讯的一个大致情况，像今天要提到的Activity启动过程，也是以Binder为通讯方式。系统对这个工作过程做了很多封装，使得启动一个Activity变得十分简单。这看似简单的背后，实际上是Activity与ActivityManagerService之间多次通讯的结果。<br>阅读该篇文章建议配合源码一起食用，味道更佳。</p><h1 id="Launcher"><a href="#Launcher" class="headerlink" title="Launcher"></a>Launcher</h1><p>手机桌面APP叫Launcher，每一个应用的icon都罗列在Launcher上，点击icon触发onItemClick事件，下面例如我们要启动某个TT App，首先我们要在AndroidManifest.xml清单文件定义默认启动的Activity信息。<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">".MainActivity"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.MAIN"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">category</span> <span class="attr">android:name</span>=<span class="string">"android.intent.category.LAUNCHER"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">activity</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>然后Launcher获取到该信息之后，启动TT APP<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//该应用的包名</span></span><br><span class="line">            String pkg = info.activityInfo.packageName;</span><br><span class="line">            <span class="comment">//应用的主activity类</span></span><br><span class="line">            String cls = info.activityInfo.name;</span><br><span class="line"></span><br><span class="line">            ComponentName componet = <span class="keyword">new</span> ComponentName(pkg, cls);</span><br><span class="line"></span><br><span class="line">            Intent i = <span class="keyword">new</span> Intent();</span><br><span class="line">            i.setComponent(componet);</span><br><span class="line">            startActivity(i);</span><br></pre></td></tr></table></figure></p><p>启动Activity这一工作不管是相同应用的2个不同Activity的启动，或者是不同进程不同应用的Activity启动，都是由Activity大管家ActivityManagerService（简称AMS）全权管理，而他们之间的通讯就要用到Binder，通过Binder与AMS多次通讯，才能启动该App。</p><h1 id="整体流程"><a href="#整体流程" class="headerlink" title="整体流程"></a>整体流程</h1><blockquote><p>通过对Android操作系统的学习可以提高我们对操作系统在技术实现上的理解，这对于加强开发人员的内功是很有帮助的。<br>但是由于Android内部实现多数都比较复杂，在研究内部实现上应该更加侧重对整体流程的把握，而不能深入到代码细节不能自拔</p></blockquote><ol><li>Launcher通知AMS启动淘宝APP的MainActivity，也就是清单文件设置启动的Activity。</li><li>AMS记录要启动的Activity信息，并且通知Launcher进入pause状态。</li><li>Launcher进入pause状态后，通知AMS已经paused了，可以启动淘宝了。</li><li>TTapp未开启过，所以AMS启动新的进程，并且在新进程中创建ActivityThread对象，执行其中的main函数方法。</li><li>TTapp主线程启动完毕后通知AMS，并传入applicationThread以便通讯。</li><li>AMS通知淘宝绑定Application并启动MainActivity。</li><li>TTapp启动MainActivitiy，并且创建和关联Context,最后调用onCreate方法。</li></ol><h2 id="startActivityForResult"><a href="#startActivityForResult" class="headerlink" title="startActivityForResult"></a>startActivityForResult</h2><p>我们从Activity的startActivity方法开始分析。 startActivity方法有好几种重载方式，但它们最终都会调用startActivityForResult方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Activity.java</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivity</span><span class="params">(Intent intent, @Nullable Bundle options)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (options != <span class="keyword">null</span>) &#123;</span><br><span class="line">            startActivityForResult(intent, -<span class="number">1</span>, options);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// Note we want to go through this call for compatibility with</span></span><br><span class="line">            <span class="comment">// applications that may have overridden the method.</span></span><br><span class="line">            startActivityForResult(intent, -<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>在startActivityForResult方法内，会调用Instrumentation的execStartActivity方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Activity.java</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            String who, Intent intent, <span class="keyword">int</span> requestCode, @Nullable Bundle options)</span> </span>&#123;</span><br><span class="line">        Uri referrer = onProvideReferrer();</span><br><span class="line">        <span class="keyword">if</span> (referrer != <span class="keyword">null</span>) &#123;</span><br><span class="line">            intent.putExtra(Intent.EXTRA_REFERRER, referrer);</span><br><span class="line">        &#125;</span><br><span class="line">        options = transferSpringboardActivityOptions(options);</span><br><span class="line">        Instrumentation.ActivityResult ar =</span><br><span class="line">            mInstrumentation.execStartActivity(</span><br><span class="line">                <span class="keyword">this</span>, mMainThread.getApplicationThread(), mToken, who,</span><br><span class="line">                intent, requestCode, options);</span><br><span class="line">        <span class="keyword">if</span> (ar != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mMainThread.sendActivityResult(</span><br><span class="line">                mToken, who, requestCode,</span><br><span class="line">                ar.getResultCode(), ar.getResultData());</span><br><span class="line">        &#125;</span><br><span class="line">        cancelInputsAndStartExitTransition(options);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><h2 id="Instrumentation"><a href="#Instrumentation" class="headerlink" title="Instrumentation"></a>Instrumentation</h2><blockquote><p>Instrumentation从字面上来看是仪器盘的意思，具体到程序中是管理activity的一个工具类，包括创建和启动Activity，activity的生命周期方法都是由Instrumentation这个仪器来控制，一个进程中只用一个Instrumentation实例。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Instrumentation.java</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Base class for implementing application instrumentation code.  When running</span></span><br><span class="line"><span class="comment"> * with instrumentation turned on, this class will be instantiated for you</span></span><br><span class="line"><span class="comment"> * before any of the application code, allowing you to monitor all of the</span></span><br><span class="line"><span class="comment"> * interaction the system has with the application.  An Instrumentation</span></span><br><span class="line"><span class="comment"> * implementation is described to the system through an AndroidManifest.xml's</span></span><br><span class="line"><span class="comment"> * &amp;lt;instrumentation&amp;gt; tag.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Instrumentation</span> </span>&#123;</span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> who The Context from which the activity is being started.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> contextThread The main thread of the Context from which the activity</span></span><br><span class="line"><span class="comment">     *                      is being started.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> token Internal token identifying to the system who is starting </span></span><br><span class="line"><span class="comment">     *              the activity; may be null.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> target Which activity is performing the start (and thus receiving </span></span><br><span class="line"><span class="comment">     *               any result);</span></span><br><span class="line"><span class="comment">     *               may be null if this call is no`t being made form an activity.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> intent The actual Intent to start.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> requestCode Identifier for this request's result; less than zero </span></span><br><span class="line"><span class="comment">     *                    if the caller is not expecting a result.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> options Addition options.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ActivityResult <span class="title">execStartActivity</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            Context who, IBinder contextThread, IBinder token, Activity target,</span></span></span><br><span class="line"><span class="function"><span class="params">            Intent intent, <span class="keyword">int</span> requestCode, Bundle options)</span> </span>&#123;</span><br><span class="line">        IApplicationThread whoThread = (IApplicationThread) contextThread;</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">int</span> result = ActivityManagerNative.getDefault()</span><br><span class="line">                .startActivity(whoThread, who.getBasePackageName(), intent,</span><br><span class="line">                        intent.resolveTypeIfNeeded(who.getContentResolver()),</span><br><span class="line">                        token, target != <span class="keyword">null</span> ? target.mEmbeddedID : <span class="keyword">null</span>,</span><br><span class="line">                        requestCode, <span class="number">0</span>, <span class="keyword">null</span>, options);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//检查启动Activity的结果（抛出异常，例如清单文件未注册Activity）</span></span><br><span class="line">            checkStartActivityResult(result, intent);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Failure from system"</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ....</span><br></pre></td></tr></table></figure><p>我们截取了比较关键的代码片段来分析Instrumentation的execStartActivity方法，方法参数注释中也有对该方法的几个参数进行简单描述。下面我们来分析一下比较重要的2个参数，contextThread和token。 </p><h3 id="IBinder-contextThread"><a href="#IBinder-contextThread" class="headerlink" title="IBinder contextThread"></a>IBinder contextThread</h3><p>在上一个方法中传入为mMainThread.getApplicationThread()<br>我们可以看到这是一个IBinder对象，说明它的作用就是用于进程间通讯的Binder对象。</p><blockquote><p>mMainThread实际上是ActivityThread对象。ActivityThread，就是主线程，也就是UI线程，它是在App启动时创建的，它代表了App应用程序。<br>啥？ActivityThread代表了App应用程序，那Application类岂不是被架空了？其实，Application对我们App开发人员来说也许很重要，但是在Android系统中还真的没那么重要，他就是个上下文。Activity不是有个Context上下文吗？Application就是整个ActivityThread的上下文。</p></blockquote><p>我们找到ActivityThread文件，其实这个getApplicationThread方法获取的是内部类ApplicationThread对象,而且ApplicationThread继承IApplicationThread.Stub<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ActivityThread</span> </span>&#123;<span class="comment">//没有继承或者实现其他类。</span></span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">final</span> ApplicationThread mAppThread = <span class="keyword">new</span> ApplicationThread();</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">public</span> ApplicationThread <span class="title">getApplicationThread</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mAppThread;</span><br><span class="line">    &#125;</span><br><span class="line">    .....</span><br><span class="line">    <span class="comment">//ActivityThread的内部类ApplicationThread </span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ApplicationThread</span> <span class="keyword">extends</span> <span class="title">IApplicationThread</span>.<span class="title">Stub</span> </span>&#123;</span><br><span class="line">    ......</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面的介绍中我们也说过，Activity的启动实际上是多次进程间通讯的成果，看到这里我们就可以得出结论：<strong>ActivityThread通过内部类ApplicationThread来进行进程间通讯.</strong></p><h3 id="IBinder-token"><a href="#IBinder-token" class="headerlink" title="IBinder token"></a>IBinder token</h3><p>追溯到参数起源，这个token对象，是在Activity的attach方法中传入的，也就是Activity的创建与关联时候(下面的内容会提到)传入的Activity信息.<br>这也是个Binder对象，它代表了Launcher这个Activity，这里也通过Instrumentation传给AMS，AMS查询后，就知道是谁向AMS发起请求了。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Activity.java</span></span><br><span class="line">    <span class="keyword">private</span> IBinder mToken;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">attach</span><span class="params">(Context context, ActivityThread aThread,</span></span></span><br><span class="line"><span class="function"><span class="params">            Instrumentation instr, IBinder token, <span class="keyword">int</span> ident,</span></span></span><br><span class="line"><span class="function"><span class="params">            Application application, Intent intent, ActivityInfo info,</span></span></span><br><span class="line"><span class="function"><span class="params">            CharSequence title, Activity parent, String id,</span></span></span><br><span class="line"><span class="function"><span class="params">            NonConfigurationInstances lastNonConfigurationInstances,</span></span></span><br><span class="line"><span class="function"><span class="params">            Configuration config, String referrer, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params">            Window window)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">        mToken = token;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>contextThread和token这两个参数是伏笔，传递给AMS，以后AMS想反过来通知Launcher，就能通过这两个参数，找到Launcher。</p></blockquote><h2 id="startActivity"><a href="#startActivity" class="headerlink" title="startActivity"></a>startActivity</h2><p>在Instrumentation中，启动Activity真正的实现是由ActivityManager.getService()的startActivity方法来完成。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ActivityResult <span class="title">execStartActivityAsCaller</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            Context who, IBinder contextThread, IBinder token, Activity target,</span></span></span><br><span class="line"><span class="function"><span class="params">            Intent intent, <span class="keyword">int</span> requestCode, Bundle options, <span class="keyword">boolean</span> ignoreTargetSecurity,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">            ...</span><br><span class="line">            <span class="keyword">int</span> result = ActivityManager.getService()</span><br><span class="line">                .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,</span><br><span class="line">                        intent.resolveTypeIfNeeded(who.getContentResolver()),</span><br><span class="line">                        token, target != <span class="keyword">null</span> ? target.mEmbeddedID : <span class="keyword">null</span>,</span><br><span class="line">                        requestCode, <span class="number">0</span>, <span class="keyword">null</span>, options, ignoreTargetSecurity, userId);</span><br><span class="line">            checkStartActivityResult(result, intent);</span><br></pre></td></tr></table></figure></p><p>在ActivityManager.java中getService()实现如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@hide</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IActivityManager <span class="title">getService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> IActivityManagerSingleton.get();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="参考资料："><a href="#参考资料：" class="headerlink" title="参考资料："></a>参考资料：</h1><p><a href="https://blog.csdn.net/qian520ao/article/details/78156214" target="_blank" rel="noopener">Android Launcher 启动 Activity 的工作过程</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;通过&lt;a href=&quot;../Android-Binder-Analysis&quot;&gt;Binder之应用层总结与分析&lt;/a&gt;可以了解到进程间通讯的一
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Launcher" scheme="http://yoursite.com/categories/Android/Launcher/"/>
    
      <category term="Activity" scheme="http://yoursite.com/categories/Android/Launcher/Activity/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Activity" scheme="http://yoursite.com/tags/Activity/"/>
    
      <category term="Launcher" scheme="http://yoursite.com/tags/Launcher/"/>
    
  </entry>
  
  <entry>
    <title>Andorid中AIDL解析</title>
    <link href="http://yoursite.com/Android-AIDL-Mechanism/"/>
    <id>http://yoursite.com/Android-AIDL-Mechanism/</id>
    <published>2018-02-16T09:52:44.000Z</published>
    <updated>2018-08-07T06:59:10.093Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>AIDL是android接口定义语言，可以利用它定义客户端与服务端进行进程间通信 (IPC) 时都认可的编程接口。跨进程通信，不同的进程，运行在不同的VM虚拟机上，每个进程都有独立的内存空间，管理各自的数据，就像“我住长江头，君住长江尾。日日思君不见君，共饮长江水”。AIDL就是“我”与“君”沟通的桥梁。</p><h2 id="关键词"><a href="#关键词" class="headerlink" title="关键词"></a>关键词</h2><p>Binder<br>AIDL:<br>序列化<br>Stub类: 字面上的翻译是存根的意思.<br>Proxy类:字面上的翻译是代理的意思.Stub的内部代理类Proxy。<br>Transaction:字面上的翻译是交易合同合约的意思.<br><img src="/Android-AIDL-Mechanism/02-AIDL-Mechanism.webp" alt="AIDL机制"></p><p>AIDL机制的核心是对aidl文件进行编译后的编译中间件java文件，上述原理图中都是在该java文件中实现，因此分析AIDL机制的重点放在对此文件的分析中.</p><p>AIDL通讯为双工，双向通讯，但还是要分为服务端和客户端两个方面：</p><p>服务端的工作：<br>a) 创建一个Service用来监听客户端的连接请求<br>b) 创建一个AIDL文件，将暴露给客户端的接口在这个AIDL文件中声明<br>c) 在Service中实现AIDL中定义的接口</p><p>客户端工作：<br>a）绑定服务端<br>b) 绑定成功后，将服务端返回的Binder对象转成AIDL接口所属类型<br>c) 调用服务端接口</p><p>使用AIDL时需要注意以下几点： <a href="http://www.jianshu.com/p/23612b2cce30" target="_blank" rel="noopener">参考</a><br>1、当非跨进程时(在Manifest没有指定process属性)，当客户端调用AIDL方法时，客户端在UI线程，那么被调用的服务端的方法也在UI线程。反之。如果。客户端调用AIDL方法时，客户端在非UI线程，那么被调用的服务端方法也在非UI线程。<br>2、当跨进程时(在Manifest指定process属性)，服务端的方法运行在Binder的线程池中，所以Binder方法不管是否耗时都应该采用同步的方式去实现。<br>3、当客户端发起远程请求时，由于当前线程会被挂起直至服务端进程返回数据，所以如果一个远程方法是很耗时的，那么不能再UI线程中发起此远程请求。<br>4、如果一个远程方法是耗时的，在客户端调用时会被挂起(上面说的第3点)。但是如果远程方法返回void，也就是远程方法在怎么耗时，客户端也不需要你返回数据，那么可以在AIDL方法中增加oneway关键字。<br>oneway void addBook(in Book book) 这样客户端就不会被挂起了。<br>5、在跨进程中，客户端传递的对象是经过序列化的，在服务端接收的对象，是经过反序列化的。服务端接收的对象只是里面的东西一样，但是根本不是同一个对象，这点要注意。<br>6、所有非原语（原语含义看下面）参数都需要指示数据走向的方向标记。可以是 in、out 或 inout。<br>原语默认为 in，不能是其他方向。<br>7、AIDL接口中只支持方法，不支持声明静态常量。</p><h1 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h1><p>拿IKeyguardService.aidl来举例<br> 使用AIDL完成通讯需要经过如下几个步骤：<br> 1) 创建 aidl 接口文件<br> 2) 创建一个远程 Service<br> 3) 客户端链接Binder<br><a href="https://www.jianshu.com/p/23612b2cce30" target="_blank" rel="noopener">下面文字参考</a></p><h2 id="创建-aidl-接口文件"><a href="#创建-aidl-接口文件" class="headerlink" title="创建 aidl 接口文件"></a>创建 aidl 接口文件</h2><h3 id="支持的参数类型"><a href="#支持的参数类型" class="headerlink" title="支持的参数类型"></a>支持的参数类型</h3><ol><li>八种基本数据类型；</li><li>String、CharSequence；</li><li>List、Map，它们中的数据类型也应该是AIDL支持的；</li><li>实现Parcelabel的引用类型。</li></ol><h3 id="自定义引用类型使用"><a href="#自定义引用类型使用" class="headerlink" title="自定义引用类型使用"></a>自定义引用类型使用</h3><p>如果要使用自定义的数据类型，需要先为它也生成一个aidl文件，里面内容只需两行：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">AIDL的包名;</span><br><span class="line">parcelable 类名;</span><br></pre></td></tr></table></figure></p><p>例如：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">package com.coorchice.coorchicelibone;</span><br><span class="line">parcelable Role;</span><br></pre></td></tr></table></figure></p><p>接着就可以创建对应的java数据类了，然后实现Parcelable接口，注意包名需要和aidl一模一样！如果你对自定义类型使用了in或者inout标识符的话，你必须再给自定义类实现readFromParcel()方法。比如：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public void readFromParcel(Parcel <span class="keyword">in</span>) &#123;</span><br><span class="line">  name = in.readString();</span><br><span class="line">  skill = in.readString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然后在定义aidl接口时，一定要记得手动写一下自定义引用类的import！</p><h3 id="参数修饰符"><a href="#参数修饰符" class="headerlink" title="参数修饰符"></a>参数修饰符</h3><p>•in: 表示参数数据只能由客户端传递到服务端，基本类型就默认只支持in修饰符。<br>•out：表示参数数据只能由服务端传递到客户端。即服务端如果修改了参数对象的值，那么客户端的值也会变化，但是服务端无法读取到客户端对象的值。<br>•inout：表示参数数据能够双向传递。</p><h3 id="定义服务接口"><a href="#定义服务接口" class="headerlink" title="定义服务接口"></a>定义服务接口</h3><p>现在，我们可以开始定义服务接口了！这里定义的服务接口就是后面我们需要在客户端调用的。我们以SystemUI模块中的IRecentsSystemUserCallbacks.aidl为例来讲解<br>frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.android.systemui.recents;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.graphics.Rect;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Due to the fact that RecentsActivity is per-user, we need to establish an</span></span><br><span class="line"><span class="comment"> * interface (this) for the non-system user to register itself for callbacks and to</span></span><br><span class="line"><span class="comment"> * callback to the system user to update internal state.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">oneway <span class="class"><span class="keyword">interface</span> <span class="title">IRecentsSystemUserCallbacks</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, in Rect initialRect)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>注意：自定义的类必须加in、out、inout等标识符，否则会报错！</p></blockquote><p>实例<br>frameworks/base/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java<br>frameworks/base/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl</p><h2 id="编译后生成的类解析"><a href="#编译后生成的类解析" class="headerlink" title="编译后生成的类解析"></a>编译后生成的类解析</h2><p>编译之后编译器自动帮我们生成了一个服务接口类名.Stub的抽象类，它继承自Binder，然后实现了我们定义的服务接口（注意我们的服务接口实现了IInterface接口）。嗯，重点是它是一个Binder！它就是我们用来进行Binder通讯的！</p><p>*.aidl文件如何被编译器编译后生成*.java文件的，<a href="https://blog.csdn.net/hahajluzxb/article/details/40544295" target="_blank" rel="noopener">请参考</a></p><p>其实，Android的AIDL就是让编译器帮助我们生成一个实现了我们接口的Binder，以帮助我们简化开发。当然，如果你了解原理的话，也可以自己写。</p><p>下面我们一步一步来看看这编译器生成的类都有些什么？<br>out/target/common/obj/APPS/SystemUI_intermediates/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is auto-generated.  DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> * Original file: frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">package</span> com.android.systemui.recents;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Due to the fact that RecentsActivity is per-user, we need to establish an</span></span><br><span class="line"><span class="comment"> * interface (this) for the non-system user to register itself for callbacks and to</span></span><br><span class="line"><span class="comment"> * callback to the system user to update internal state.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IRecentsSystemUserCallbacks</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">IInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="comment">/** Local-side IPC implementation stub class. */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Stub</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">systemui</span>.<span class="title">recents</span>.<span class="title">IRecentsSystemUserCallbacks</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> java.lang.String DESCRIPTOR = <span class="string">"com.android.systemui.recents.IRecentsSystemUserCallbacks"</span>;</span><br><span class="line"><span class="comment">/** Construct the stub at attach it to the interface. */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Stub</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">this</span>.attachInterface(<span class="keyword">this</span>, DESCRIPTOR);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Cast an IBinder object into an com.android.systemui.recents.IRecentsSystemUserCallbacks interface,</span></span><br><span class="line"><span class="comment"> * generating a proxy if needed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> com.android.systemui.recents.<span class="function">IRecentsSystemUserCallbacks <span class="title">asInterface</span><span class="params">(android.os.IBinder obj)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> ((obj==<span class="keyword">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">if</span> (((iin!=<span class="keyword">null</span>)&amp;&amp;(iin <span class="keyword">instanceof</span> com.android.systemui.recents.IRecentsSystemUserCallbacks))) &#123;</span><br><span class="line"><span class="keyword">return</span> ((com.android.systemui.recents.IRecentsSystemUserCallbacks)iin);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> com.android.systemui.recents.IRecentsSystemUserCallbacks.Stub.Proxy(obj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTransact</span><span class="params">(<span class="keyword">int</span> code, android.os.Parcel data, android.os.Parcel reply, <span class="keyword">int</span> flags)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">switch</span> (code)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">case</span> INTERFACE_TRANSACTION:</span><br><span class="line">&#123;</span><br><span class="line">reply.writeString(DESCRIPTOR);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_registerNonSystemUserCallbacks:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line">android.os.IBinder _arg0;</span><br><span class="line">_arg0 = data.readStrongBinder();</span><br><span class="line"><span class="keyword">int</span> _arg1;</span><br><span class="line">_arg1 = data.readInt();</span><br><span class="line"><span class="keyword">this</span>.registerNonSystemUserCallbacks(_arg0, _arg1);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_updateRecentsVisibility:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">boolean</span> _arg0;</span><br><span class="line">_arg0 = (<span class="number">0</span>!=data.readInt());</span><br><span class="line"><span class="keyword">this</span>.updateRecentsVisibility(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_startScreenPinning:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">int</span> _arg0;</span><br><span class="line">_arg0 = data.readInt();</span><br><span class="line"><span class="keyword">this</span>.startScreenPinning(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendRecentsDrawnEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">this</span>.sendRecentsDrawnEvent();</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendDockingTopTaskEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">int</span> _arg0;</span><br><span class="line">_arg0 = data.readInt();</span><br><span class="line">android.graphics.Rect _arg1;</span><br><span class="line"><span class="keyword">if</span> ((<span class="number">0</span>!=data.readInt())) &#123;</span><br><span class="line">_arg1 = android.graphics.Rect.CREATOR.createFromParcel(data);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">_arg1 = <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">this</span>.sendDockingTopTaskEvent(_arg0, _arg1);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendLaunchRecentsEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">this</span>.sendLaunchRecentsEvent();</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_setWaitingForTransitionStartEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">boolean</span> _arg0;</span><br><span class="line">_arg0 = (<span class="number">0</span>!=data.readInt());</span><br><span class="line"><span class="keyword">this</span>.setWaitingForTransitionStartEvent(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">super</span>.onTransact(code, data, reply, flags);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Proxy</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">systemui</span>.<span class="title">recents</span>.<span class="title">IRecentsSystemUserCallbacks</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="keyword">private</span> android.os.IBinder mRemote;</span><br><span class="line">Proxy(android.os.IBinder remote)</span><br><span class="line">&#123;</span><br><span class="line">mRemote = remote;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> mRemote;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> java.lang.<span class="function">String <span class="title">getInterfaceDescriptor</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> DESCRIPTOR;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(android.os.IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeStrongBinder(nonSystemUserCallbacks);</span><br><span class="line">_data.writeInt(userId);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_registerNonSystemUserCallbacks, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(((visible)?(<span class="number">1</span>):(<span class="number">0</span>)));</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_updateRecentsVisibility, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(taskId);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_startScreenPinning, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendRecentsDrawnEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, android.graphics.Rect initialRect)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(dragMode);</span><br><span class="line"><span class="keyword">if</span> ((initialRect!=<span class="keyword">null</span>)) &#123;</span><br><span class="line">_data.writeInt(<span class="number">1</span>);</span><br><span class="line">initialRect.writeToParcel(_data, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">_data.writeInt(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendDockingTopTaskEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendLaunchRecentsEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(((waitingForTransitionStart)?(<span class="number">1</span>):(<span class="number">0</span>)));</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_setWaitingForTransitionStartEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_registerNonSystemUserCallbacks = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">0</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_updateRecentsVisibility = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">1</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_startScreenPinning = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">2</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendRecentsDrawnEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">3</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendDockingTopTaskEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">4</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendLaunchRecentsEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">5</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_setWaitingForTransitionStartEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">6</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(android.os.IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, android.graphics.Rect initialRect)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="服务接口实现IInterface"><a href="#服务接口实现IInterface" class="headerlink" title="服务接口实现IInterface"></a>服务接口实现IInterface</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IRecentsSystemUserCallbacks</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">IInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译器根据我们写的服务接口，重新生成了一个接口，唯一的区别就是新的接口继承了IInterface接口。这个接口是干什么的呢？<br>我们来看frameworks/base/core/java/android/os/IInterface.java的实现.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> android.os;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Base class for Binder interfaces.  When defining a new interface,</span></span><br><span class="line"><span class="comment"> * you must derive it from IInterface.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Retrieve the Binder object associated with this interface.</span></span><br><span class="line"><span class="comment">     * You must use this instead of a plain cast, so that proxy objects</span></span><br><span class="line"><span class="comment">     * can return the correct result.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> IBinder <span class="title">asBinder</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到，它只有一个接口方法。这个方法用来定义将实现接口的类应该具备返回一个与之相关联的Binder的功能，以提供通讯能力。一般实现这个方法的类自己本身就会去继承Binder。<br>这样的设计使得Binder机制不用关心具体的接口是什么，只要是IInterface就行。事实上相当于是我们在IInterface接口的基础上扩展了接口功能，本质上还是一个IInterface，所以Binder能够认出它。</p><h3 id="继承Binder，实现服务接口"><a href="#继承Binder，实现服务接口" class="headerlink" title="继承Binder，实现服务接口"></a>继承Binder，实现服务接口</h3><p>要进行Binder通讯，我们自然需要一个Binder；要实现我们定义的服务接口功能，自然就需要实现服务接口。那么需要满足这两个条件怎么办？很简单，继承Binder，然后实现服务接口就行。<br>编译器为我们生成的类中有一个内部类Stub就是这么干的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Stub</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">systemui</span>.<span class="title">recents</span>.<span class="title">IRecentsSystemUserCallbacks</span></span>&#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="DESCRIPTOR"><a href="#DESCRIPTOR" class="headerlink" title="DESCRIPTOR"></a>DESCRIPTOR</h3><p>Binder需要绑定服务接口，定义DESCRIPTOR描述<br>为了一个Binder和一个特定服务接口绑定，以对外提供功能，需要给Binder定义一个DESCRIPTOR描述，表示我这个Binder是提供特定功能链接的，不是随便可以用的。<br>通常，DESCRIPTOR描述会直接使用包名 + 服务接口。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> java.lang.String DESCRIPTOR = <span class="string">"com.android.systemui.recents.IRecentsSystemUserCallbacks"</span>;</span><br></pre></td></tr></table></figure></p><h3 id="实现asInterface-供客户端调用"><a href="#实现asInterface-供客户端调用" class="headerlink" title="实现asInterface()供客户端调用"></a>实现asInterface()供客户端调用</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Cast an IBinder object into an com.android.systemui.recents.IRecentsSystemUserCallbacks interface,</span></span><br><span class="line"><span class="comment"> * generating a proxy if needed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> com.android.systemui.recents.<span class="function">IRecentsSystemUserCallbacks <span class="title">asInterface</span><span class="params">(android.os.IBinder obj)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> ((obj==<span class="keyword">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);</span><br><span class="line"><span class="comment">//如果在同一个进程中，则返回IRecentsSystemUserCallbacks对象</span></span><br><span class="line"><span class="keyword">if</span> (((iin!=<span class="keyword">null</span>)&amp;&amp;(iin <span class="keyword">instanceof</span> com.android.systemui.recents.IRecentsSystemUserCallbacks))) &#123;</span><br><span class="line"><span class="keyword">return</span> ((com.android.systemui.recents.IRecentsSystemUserCallbacks)iin);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//如果不在同一个进程中，则需要创建代理.</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> com.android.systemui.recents.IRecentsSystemUserCallbacks.Stub.Proxy(obj);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>作为一个服务提供者，为了能够给调用者提供远程功能，自然需要能够提供和远程服务关联的Binder来通讯，请求服务。获取Binder的接口就是IInterface中定义的。<br>先查询一下获取到的和远程Service通讯的Binde中是否已经添加了功能接口的实现，<strong>如果没有则创建代理，通过代理间接的操作Binder和远程Service通讯，实现功能。</strong></p><p><strong>思考：既然我们已经获取到了能够直接和远程Service通讯的Binder，为什么不直接操作它去向远程Service请求服务呢？</strong></p><p>确实，我们可以直接操作Binder向远程Service请求服务，但这过程中有很多繁琐的操作，还有一些code码的区别，如果不封装隔离的话，随着功能的扩展，我们将很难再去维护这段通讯逻辑。还有就是通过这种方式统一的管理通讯逻辑使得它可以随处使用，而不用没一个要用的地方都去写一遍。</p><p>既然是要代理和远程服务通讯，而且通讯的目的是为了请求服务接口定义的功能，那么很自然就能想到去实现服务接口，然后再对应的接口方法中实现逻辑即可。这样就可以分开来维护客户端和服务端的对应的每个功能了。</p><p>所以，我们的代理也需要实现服务接口，然后在代理中操作远程通讯的Binder进行通讯。</p><h3 id="重写onTransact"><a href="#重写onTransact" class="headerlink" title="重写onTransact()"></a>重写onTransact()</h3><h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><p>先用上图整体描述这个AIDL从客户端(Client)发起请求至服务端(Server)相应的工作流程，我们可以看出整体的核心就是 Binder.如下：<br><img src="/Android-AIDL-Mechanism/01-AIDL-Mechanism.webp" alt="AIDL机制原理"></p><p>在SystemUI的Recent模块中，每个用户都拥有一个Recent模块的SystemUI进程.这里的每个用户分两种，一种是System用户，一种是访客模式的用户.<br>android自从把最近任务改为一个activity后，最近任务的内部逻辑的复杂程度就在不停地快速增长着。android是支持多用户的，最近任务在每个用户空间都有一个单独运行的进程。而只有主用户空间的SystemUI进程通过AIDL才能收到PhoneWindowManager发过来的事件，比如showRecents，hideRecents等，所以副用户空间的systemui进程就需要主用户空间的systemui来通知副用户的systemui来做显示最近任务和隐藏最近任务等操作。所以会在SystemUI里面看到有两次的AIDL操作.</p><p>对于SystemUser，在Recent.java中收到来自PhoneWindwoManager的showRecentApps(),hideRecentApps()等操作时，会调用RecentImpl.java来处理.<br>在Recent.java的onStart函数中，对于SystemUser和SecondaryUser处理如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>&#123;</span><br><span class="line">...</span><br><span class="line"><span class="comment">// Due to the fact that RecentsActivity is per-user, we need to establish and interface for</span></span><br><span class="line">    <span class="comment">// the system user's Recents component to pass events (like show/hide/toggleRecents) to the</span></span><br><span class="line">    <span class="comment">// secondary user, and vice versa (like visibility change, screen pinning).</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> processUser = sSystemServicesProxy.getProcessUser();</span><br><span class="line">    <span class="keyword">if</span> (sSystemServicesProxy.isSystemUser(processUser)) &#123;</span><br><span class="line">        <span class="comment">// For the system user, initialize an instance of the interface that we can pass to the</span></span><br><span class="line">        <span class="comment">// secondary user</span></span><br><span class="line">        getComponent(CommandQueue.class).addCallbacks(<span class="keyword">this</span>);</span><br><span class="line">        mSystemToUserCallbacks = <span class="keyword">new</span> RecentsSystemUser(mContext, mImpl);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// For the secondary user, bind to the primary user's service to get a persistent</span></span><br><span class="line">        <span class="comment">// interface to register its implementation and to later update its state</span></span><br><span class="line">        registerWithSystemUser();</span><br><span class="line">    &#125;</span><br><span class="line">    putComponent(Recents.class, <span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 如果是SystemUser的话，会准备好SecondaryUser需要用到的回调mSystemToUserCallbacks，我们看RecentsSystemUser知道是一个AIDL来实现.此时通过mSystemToUserCallbacks<br> 建立了SystemUser与SecondaryUser之间调用关系的服务端，等待客户端的调用.<br> 如果是SecondaryUser的话，执行registerWithSystemUser函数，通过mSystemToUserCallbacks来注册RecentsImplProxy，RecentsImplProxy也是一个AIDL.</p><p>第一次：SystemUI进程与主用户的SystemUI进程之间的通信.<br>在IRecentsSystemUserCallbacks.aidl中<br> Server端：RecentsSystemUser.java<br> Client端：Recents.java</p><p> 此次AIDL涉及到的文件有如下：<br> 1) AIDL接口定义： frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl<br> 2) 编译的中间件：<br>    out/target/common/obj/APPS/SystemUI_intermediates/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.java<br> 3) 接口的实现： frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java<br> 4) 接口的使用：frameworks/base/packages/SystemUI/src/com/android/systemui/recents/Recents.java</p><p>第二次：访客SystemUI进程与主用户SystemUI进程之间的通信.<br> Q:为何访客SystemUI进程不能直接与SystemUI进程通信？？？？</p><p>1) 接口定义： frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl<br>2）编译中间件：<br>   out/target/common/obj/APPS/SystemUI_intermediates/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.java<br>3）接口实现：frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java<br>4）接口使用：frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java</p><p>IRecentsNonSystemUserCallbacks.aidl中<br> Server端：RecentsImplProxy.java<br> Client端：RecentsSystemUser.java</p><p>根据原理图，我们将按照一次Client的一次远程请求的流程开始分析.</p><h2 id="客户端接口的使用"><a href="#客户端接口的使用" class="headerlink" title="客户端接口的使用"></a>客户端接口的使用</h2><p>客户端 Recent.java开始调用<br>frameworks/base/packages/SystemUI/src/com/android/systemui/recents/Recents.java</p><ol><li><p>IRecentsSystemUserCallbacks的声明</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * An implementation of the SystemUI recents component, which supports both system and secondary</span></span><br><span class="line"><span class="comment"> * users.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Recents</span> <span class="keyword">extends</span> <span class="title">SystemUI</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">RecentsComponent</span>, <span class="title">CommandQueue</span>.<span class="title">Callbacks</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String TAG = <span class="string">"Recents"</span>;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">// Only for secondary users, this is the callbacks instance provided by the system user to make</span></span><br><span class="line">    <span class="comment">// calls back</span></span><br><span class="line">    <span class="keyword">private</span> IRecentsSystemUserCallbacks mUserToSystemCallbacks;</span><br></pre></td></tr></table></figure></li><li><p>Client发起请求,以Recent其中的一个onBusEvent为例来分析.可以看到对于非主用户，会新起一个线程.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Handle Recents activity visibility changed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">onBusEvent</span><span class="params">(<span class="keyword">final</span> RecentsVisibilityChangedEvent event)</span> </span>&#123;</span><br><span class="line">    SystemServicesProxy ssp = Recents.getSystemServices();</span><br><span class="line">    <span class="keyword">int</span> processUser = ssp.getProcessUser();<span class="comment">//获取进程的User ID</span></span><br><span class="line">    <span class="keyword">if</span> (ssp.isSystemUser(processUser)) &#123;<span class="comment">//如果是主用户进程，直接调用RecentImpl.java里面onVisibilityChanged函数处理</span></span><br><span class="line">        mImpl.onVisibilityChanged(event.applicationContext, event.visible);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;<span class="comment">//如果是非主用户进程,则需要新起线程处理</span></span><br><span class="line">        postToSystemUser(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    mUserToSystemCallbacks.updateRecentsVisibility(event.visible);<span class="comment">//private IRecentsSystemUserCallbacks mUserToSystemCallbacks;</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                    Log.e(TAG, <span class="string">"Callback failed"</span>, e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// This will catch the cases when a user launches from recents to another app</span></span><br><span class="line">    <span class="comment">// (and vice versa) that is not in the recents stack (such as home or bugreport) and it</span></span><br><span class="line">    <span class="comment">// would not reset the wait for transition flag. This will catch it and make sure that the</span></span><br><span class="line">    <span class="comment">// flag is reset.</span></span><br><span class="line">    <span class="keyword">if</span> (!event.visible) &#123;</span><br><span class="line">        mImpl.setWaitingForTransitionStart(<span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再看看postToSystemUser函数的实现.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Runs the runnable in the system user's Recents context, connecting to the service if</span></span><br><span class="line"><span class="comment"> * necessary.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">postToSystemUser</span><span class="params">(<span class="keyword">final</span> Runnable onConnectRunnable)</span> </span>&#123;</span><br><span class="line">    mOnConnectRunnables.add(onConnectRunnable);</span><br><span class="line">    <span class="keyword">if</span> (mUserToSystemCallbacks == <span class="keyword">null</span>) &#123;</span><br><span class="line">        Intent systemUserServiceIntent = <span class="keyword">new</span> Intent();</span><br><span class="line">        systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);</span><br><span class="line">        <span class="keyword">boolean</span> bound = mContext.bindServiceAsUser(systemUserServiceIntent,</span><br><span class="line">                mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);</span><br><span class="line">        EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,</span><br><span class="line">                EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,</span><br><span class="line">                sSystemServicesProxy.getProcessUser());</span><br><span class="line">        <span class="keyword">if</span> (!bound) &#123;</span><br><span class="line">            <span class="comment">// Retry after a fixed duration</span></span><br><span class="line">            mHandler.postDelayed(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    registerWithSystemUser();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, BIND_TO_SYSTEM_USER_RETRY_DELAY);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        runAndFlushOnConnectRunnables();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面两块代码可以看到postToSystemUser函数做了两件事件：<br>a)当mUserToSystemCallbacks即IRecentsSystemUserCallbacks的对象为空时，通过bindService函数，绑定RecentsSystemUserService这个服务,我们可以从 RecentsSystemUserService的onBind函数看到，此服务调用的是Recent.java<br>在bindService绑定时， 会调用主线程中的registerWithSystemUser函数，<br>b)如果mUserToSystemCallbacks不为空，即对主用户进程的回调存在，则执行mUserToSystemCallbacks.updateRecentsVisibility(event.visible);调用过程下面等会儿继续讲.</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//RecentsSystemUserService.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RecentsSystemUserService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"RecentsSystemUserService"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> DEBUG = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> IBinder <span class="title">onBind</span><span class="params">(Intent intent)</span> </span>&#123;</span><br><span class="line">        SystemUIApplication app = (SystemUIApplication) getApplication();</span><br><span class="line">        Recents recents = app.getComponent(Recents.class);</span><br><span class="line">        <span class="keyword">if</span> (DEBUG) &#123;</span><br><span class="line">            Log.d(TAG, <span class="string">"onBind: "</span> + recents);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (recents != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> recents.getSystemUserCallbacks();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 再来看看registerWithSystemUser函数<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></p><ol start="3"><li><p>连接服务,并绑定服务端</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Only for secondary users, this is the service connection we use to connect to the system user</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ServiceConnection mUserToSystemServiceConnection = <span class="keyword">new</span> ServiceConnection() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceConnected</span><span class="params">(ComponentName name, IBinder service)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (service != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//第一步：获取IUserToSystemCallbacks对象</span></span><br><span class="line">            mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(</span><br><span class="line">                    service);</span><br><span class="line">            <span class="comment">//第二步：写入SYSUI_RECENTS_CONNECTION事件，此处与AIDL无关，仅写入事件</span></span><br><span class="line">            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,</span><br><span class="line">                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,</span><br><span class="line">                    sSystemServicesProxy.getProcessUser());</span><br><span class="line"></span><br><span class="line">            <span class="comment">//第三步：设置主用户Binder对象代理，监听主用户的IBinder,如果死亡，则说明开始启动访客模式</span></span><br><span class="line">            <span class="comment">// Listen for system user's death, so that we can reconnect later</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                service.linkToDeath(mUserToSystemCallbacksDeathRcpt, <span class="number">0</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">                Log.e(TAG, <span class="string">"Lost connection to (System) SystemUI"</span>, e);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//第四步：刷新进程中的线程</span></span><br><span class="line">            <span class="comment">// Run each of the queued runnables</span></span><br><span class="line">            runAndFlushOnConnectRunnables();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Unbind ourselves now that we've registered our callbacks.  The</span></span><br><span class="line">        <span class="comment">// binder to the system user are still valid at this point.</span></span><br><span class="line">        mContext.unbindService(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceDisconnected</span><span class="params">(ComponentName name)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Do nothing</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>从上述代码来看，连接服务做了四件事情，其中的重点是第二步,mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(service);这句代码中我们重点关注<strong>asInterface</strong>,从后面的代码，我们可以看到我们可以IRecentsSystemUserCallbacks.Stub是IRecentsSystemUserCallbacks接口的内部抽象类.<br><strong>asInterface</strong>用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象，这种转换过程是区分进程的【如果客户端和服务端位于同一进程，那么此方法返回的就是服务端的Stub对象本身，否则返回的是系统封装后的Stub.proxy对象】<br>在 ServiceConnection 绑定服务的方法里，我们通过IRecentsSystemUserCallbacks.Stub.asInterface(service)方法 获得 IRecentsSystemUserCallbacks对象，我们跟着 asInterface 步伐看看里面有什么名堂。<br>我们到编译中间件IRecentsSystemUserCallbacks.java中看下asInterface函数的实现:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Cast an IBinder object into an com.android.systemui.recents.IRecentsSystemUserCallbacks interface,</span></span><br><span class="line"><span class="comment"> * generating a proxy if needed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> com.android.systemui.recents.<span class="function">IRecentsSystemUserCallbacks <span class="title">asInterface</span><span class="params">(android.os.IBinder obj)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> ((obj==<span class="keyword">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">if</span> (((iin!=<span class="keyword">null</span>)&amp;&amp;(iin <span class="keyword">instanceof</span> com.android.systemui.recents.IRecentsSystemUserCallbacks))) &#123;</span><br><span class="line"><span class="keyword">return</span> ((com.android.systemui.recents.IRecentsSystemUserCallbacks)iin);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> com.android.systemui.recents.IRecentsSystemUserCallbacks.Stub.Proxy(obj);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们看看DESCRIPTOR的定义：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> java.lang.String DESCRIPTOR = <span class="string">"com.android.systemui.recents.IRecentsSystemUserCallbacks"</span>;</span><br></pre></td></tr></table></figure><p>因此，可以看出如果是同一进程，那么就返回Stub对象本身(obj.queryLocalInterface(DESCRIPTOR))，否则如果是跨进程则返回Stub的代理内部类Proxy。<br>也就是说这个 asInterface 方法返回的是一个远程接口具备的能力（有什么方法可以调用），在我们项目里，asInterface 的能力就是 get/setDemand 和 注册/解绑监听接口。</p></li></ol><p>主用户IBinder死亡，开始启动访客模式SystemUI.这里就是开启了另外一个AIDL，等会儿讲.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Only for secondary users, this is the death handler for the binder from the system user</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = <span class="keyword">new</span> IBinder.DeathRecipient() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">binderDied</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mUserToSystemCallbacks = <span class="keyword">null</span>;</span><br><span class="line">        EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,</span><br><span class="line">                EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,</span><br><span class="line">                sSystemServicesProxy.getProcessUser());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Retry after a fixed duration</span></span><br><span class="line">        mHandler.postDelayed(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                registerWithSystemUser();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, BIND_TO_SYSTEM_USER_RETRY_DELAY);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><h2 id="接口定义："><a href="#接口定义：" class="headerlink" title="接口定义："></a>接口定义：</h2><p>frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.android.systemui.recents;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.graphics.Rect;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Due to the fact that RecentsActivity is per-user, we need to establish an</span></span><br><span class="line"><span class="comment"> * interface (this) for the non-system user to register itself for callbacks and to</span></span><br><span class="line"><span class="comment"> * callback to the system user to update internal state.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">oneway <span class="class"><span class="keyword">interface</span> <span class="title">IRecentsSystemUserCallbacks</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, in Rect initialRect)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="接口中间件"><a href="#接口中间件" class="headerlink" title="接口中间件"></a>接口中间件</h2><p>out/target/common/obj/APPS/SystemUI_intermediates/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This file is auto-generated.  DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> * Original file: frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">package</span> com.android.systemui.recents;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Due to the fact that RecentsActivity is per-user, we need to establish an</span></span><br><span class="line"><span class="comment"> * interface (this) for the non-system user to register itself for callbacks and to</span></span><br><span class="line"><span class="comment"> * callback to the system user to update internal state.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IRecentsSystemUserCallbacks</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">IInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="comment">/** Local-side IPC implementation stub class. */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Stub</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Binder</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">systemui</span>.<span class="title">recents</span>.<span class="title">IRecentsSystemUserCallbacks</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> java.lang.String DESCRIPTOR = <span class="string">"com.android.systemui.recents.IRecentsSystemUserCallbacks"</span>;</span><br><span class="line"><span class="comment">/** Construct the stub at attach it to the interface. */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Stub</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">this</span>.attachInterface(<span class="keyword">this</span>, DESCRIPTOR);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Cast an IBinder object into an com.android.systemui.recents.IRecentsSystemUserCallbacks interface,</span></span><br><span class="line"><span class="comment"> * generating a proxy if needed.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> com.android.systemui.recents.<span class="function">IRecentsSystemUserCallbacks <span class="title">asInterface</span><span class="params">(android.os.IBinder obj)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> ((obj==<span class="keyword">null</span>)) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">if</span> (((iin!=<span class="keyword">null</span>)&amp;&amp;(iin <span class="keyword">instanceof</span> com.android.systemui.recents.IRecentsSystemUserCallbacks))) &#123;</span><br><span class="line"><span class="keyword">return</span> ((com.android.systemui.recents.IRecentsSystemUserCallbacks)iin);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> com.android.systemui.recents.IRecentsSystemUserCallbacks.Stub.Proxy(obj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTransact</span><span class="params">(<span class="keyword">int</span> code, android.os.Parcel data, android.os.Parcel reply, <span class="keyword">int</span> flags)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">switch</span> (code)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">case</span> INTERFACE_TRANSACTION:</span><br><span class="line">&#123;</span><br><span class="line">reply.writeString(DESCRIPTOR);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_registerNonSystemUserCallbacks:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line">android.os.IBinder _arg0;</span><br><span class="line">_arg0 = data.readStrongBinder();</span><br><span class="line"><span class="keyword">int</span> _arg1;</span><br><span class="line">_arg1 = data.readInt();</span><br><span class="line"><span class="keyword">this</span>.registerNonSystemUserCallbacks(_arg0, _arg1);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_updateRecentsVisibility:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">boolean</span> _arg0;</span><br><span class="line">_arg0 = (<span class="number">0</span>!=data.readInt());</span><br><span class="line"><span class="keyword">this</span>.updateRecentsVisibility(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_startScreenPinning:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">int</span> _arg0;</span><br><span class="line">_arg0 = data.readInt();</span><br><span class="line"><span class="keyword">this</span>.startScreenPinning(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendRecentsDrawnEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">this</span>.sendRecentsDrawnEvent();</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendDockingTopTaskEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">int</span> _arg0;</span><br><span class="line">_arg0 = data.readInt();</span><br><span class="line">android.graphics.Rect _arg1;</span><br><span class="line"><span class="keyword">if</span> ((<span class="number">0</span>!=data.readInt())) &#123;</span><br><span class="line">_arg1 = android.graphics.Rect.CREATOR.createFromParcel(data);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">_arg1 = <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">this</span>.sendDockingTopTaskEvent(_arg0, _arg1);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_sendLaunchRecentsEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">this</span>.sendLaunchRecentsEvent();</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> TRANSACTION_setWaitingForTransitionStartEvent:</span><br><span class="line">&#123;</span><br><span class="line">data.enforceInterface(DESCRIPTOR);</span><br><span class="line"><span class="keyword">boolean</span> _arg0;</span><br><span class="line">_arg0 = (<span class="number">0</span>!=data.readInt());</span><br><span class="line"><span class="keyword">this</span>.setWaitingForTransitionStartEvent(_arg0);</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">super</span>.onTransact(code, data, reply, flags);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Proxy</span> <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">systemui</span>.<span class="title">recents</span>.<span class="title">IRecentsSystemUserCallbacks</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="keyword">private</span> android.os.IBinder mRemote;</span><br><span class="line">Proxy(android.os.IBinder remote)</span><br><span class="line">&#123;</span><br><span class="line">mRemote = remote;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="keyword">public</span> android.os.<span class="function">IBinder <span class="title">asBinder</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> mRemote;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> java.lang.<span class="function">String <span class="title">getInterfaceDescriptor</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> DESCRIPTOR;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(android.os.IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeStrongBinder(nonSystemUserCallbacks);</span><br><span class="line">_data.writeInt(userId);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_registerNonSystemUserCallbacks, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(((visible)?(<span class="number">1</span>):(<span class="number">0</span>)));</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_updateRecentsVisibility, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(taskId);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_startScreenPinning, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendRecentsDrawnEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, android.graphics.Rect initialRect)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(dragMode);</span><br><span class="line"><span class="keyword">if</span> ((initialRect!=<span class="keyword">null</span>)) &#123;</span><br><span class="line">_data.writeInt(<span class="number">1</span>);</span><br><span class="line">initialRect.writeToParcel(_data, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">_data.writeInt(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendDockingTopTaskEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_sendLaunchRecentsEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span> <span class="keyword">throws</span> android.os.RemoteException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">android.os.Parcel _data = android.os.Parcel.obtain();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">_data.writeInterfaceToken(DESCRIPTOR);</span><br><span class="line">_data.writeInt(((waitingForTransitionStart)?(<span class="number">1</span>):(<span class="number">0</span>)));</span><br><span class="line">mRemote.transact(Stub.TRANSACTION_setWaitingForTransitionStartEvent, _data, <span class="keyword">null</span>, android.os.IBinder.FLAG_ONEWAY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">_data.recycle();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_registerNonSystemUserCallbacks = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">0</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_updateRecentsVisibility = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">1</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_startScreenPinning = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">2</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendRecentsDrawnEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">3</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendDockingTopTaskEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">4</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_sendLaunchRecentsEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">5</span>);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TRANSACTION_setWaitingForTransitionStartEvent = (android.os.IBinder.FIRST_CALL_TRANSACTION + <span class="number">6</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(android.os.IBinder nonSystemUserCallbacks, <span class="keyword">int</span> userId)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, android.graphics.Rect initialRect)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span> <span class="keyword">throws</span> android.os.RemoteException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="接口的实现"><a href="#接口的实现" class="headerlink" title="接口的实现"></a>接口的实现</h2><p>frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.android.systemui.recents;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Rect;</span><br><span class="line"><span class="keyword">import</span> android.os.IBinder;</span><br><span class="line"><span class="keyword">import</span> android.os.RemoteException;</span><br><span class="line"><span class="keyword">import</span> android.util.EventLog;</span><br><span class="line"><span class="keyword">import</span> android.util.Log;</span><br><span class="line"><span class="keyword">import</span> android.util.SparseArray;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.android.systemui.EventLogConstants;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.EventLogTags;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.events.EventBus;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.events.activity.DockedTopTaskEvent;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.events.ui.RecentsDrawnEvent;</span><br><span class="line"><span class="keyword">import</span> com.android.systemui.recents.misc.ForegroundThread;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * An implementation of the system user's Recents interface to be called remotely by secondary</span></span><br><span class="line"><span class="comment"> * users.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RecentsSystemUser</span> <span class="keyword">extends</span> <span class="title">IRecentsSystemUserCallbacks</span>.<span class="title">Stub</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"RecentsSystemUser"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Context mContext;</span><br><span class="line">    <span class="keyword">private</span> RecentsImpl mImpl;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> SparseArray&lt;IRecentsNonSystemUserCallbacks&gt; mNonSystemUserRecents =</span><br><span class="line">            <span class="keyword">new</span> SparseArray&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">RecentsSystemUser</span><span class="params">(Context context, RecentsImpl impl)</span> </span>&#123;</span><br><span class="line">        mContext = context;</span><br><span class="line">        mImpl = impl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerNonSystemUserCallbacks</span><span class="params">(<span class="keyword">final</span> IBinder nonSystemUserCallbacks,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">final</span> <span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">final</span> IRecentsNonSystemUserCallbacks callback =</span><br><span class="line">                    IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);</span><br><span class="line">            nonSystemUserCallbacks.linkToDeath(<span class="keyword">new</span> IBinder.DeathRecipient() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">binderDied</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));</span><br><span class="line">                    EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,</span><br><span class="line">                            EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_UNREGISTER_USER,</span><br><span class="line">                            userId);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="number">0</span>);</span><br><span class="line">            mNonSystemUserRecents.put(userId, callback);</span><br><span class="line">            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,</span><br><span class="line">                    EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_REGISTER_USER, userId);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RemoteException e) &#123;</span><br><span class="line">            Log.e(TAG, <span class="string">"Failed to register NonSystemUserCallbacks"</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> IRecentsNonSystemUserCallbacks <span class="title">getNonSystemUserRecentsForUser</span><span class="params">(<span class="keyword">int</span> userId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mNonSystemUserRecents.get(userId);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateRecentsVisibility</span><span class="params">(<span class="keyword">boolean</span> visible)</span> </span>&#123;</span><br><span class="line">        ForegroundThread.getHandler().post(() -&gt; &#123;</span><br><span class="line">            mImpl.onVisibilityChanged(mContext, visible);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startScreenPinning</span><span class="params">(<span class="keyword">int</span> taskId)</span> </span>&#123;</span><br><span class="line">        ForegroundThread.getHandler().post(() -&gt; &#123;</span><br><span class="line">            mImpl.onStartScreenPinning(mContext, taskId);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendRecentsDrawnEvent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        EventBus.getDefault().post(<span class="keyword">new</span> RecentsDrawnEvent());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendDockingTopTaskEvent</span><span class="params">(<span class="keyword">int</span> dragMode, Rect initialRect)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">        EventBus.getDefault().post(<span class="keyword">new</span> DockedTopTaskEvent(dragMode, initialRect));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendLaunchRecentsEvent</span><span class="params">()</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">        EventBus.getDefault().post(<span class="keyword">new</span> RecentsActivityStartingEvent());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setWaitingForTransitionStartEvent</span><span class="params">(<span class="keyword">boolean</span> waitingForTransitionStart)</span> </span>&#123;</span><br><span class="line">        EventBus.getDefault().post(<span class="keyword">new</span> SetWaitingForTransitionStartEvent(</span><br><span class="line">                waitingForTransitionStart));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>1) 接口定义： frameworks/base/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl<br>2）编译中间件：<br>3）接口实现：frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java<br>4）接口使用：</p><p>Q:很好奇AIDL文件写好后是如何编译成java文件的？<br> 待后续继续研究.</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/qian520ao/article/details/78072250" target="_blank" rel="noopener">Android 深入浅出AIDL（一）</a><br><a href="https://blog.csdn.net/qian520ao/article/details/78074983" target="_blank" rel="noopener">Android 深入浅出AIDL（二）</a></p><p><a href="https://blog.csdn.net/luoyanglizi/article/details/51980630" target="_blank" rel="noopener">Android：学习AIDL，这一篇文章就够了(上)</a><br><a href="https://blog.csdn.net/luoyanglizi/article/details/52029091" target="_blank" rel="noopener">Android：学习AIDL，这一篇文章就够了(下)</a></p><p><a href="https://blog.csdn.net/xingchenxuanfeng/article/details/77895655" target="_blank" rel="noopener">android 最近任务多进程调度逻辑分析</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&amp;mid=2650241680&amp;idx=1&amp;sn=14648a37ff1e7844e33cd548b9ec5a71&amp;chksm=88638bffbf1402e9b6dbe6b186bc619a341fc5fe0fd2aefd4a7dd3ac70d0539573c48863de0f#rd" target="_blank" rel="noopener">带你一起剖析Android AIDL跨进程通信的实现原理</a></p><p>实例<br><a href="https://mp.weixin.qq.com/s?__biz=MzI1MjMyOTU2Ng==&amp;mid=2247484289&amp;idx=1&amp;sn=aa92cacd43a7d36bab0fd036ad64e79d&amp;chksm=e9e428b0de93a1a6f7989f7a3c27ea0791c03c79b0cf385adf03c394eb189ab4d265f4b6b275#rd" target="_blank" rel="noopener">Android基础进阶之学习使用AIDL进行跨进程通信</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzU3NTA3MDU1OQ==&amp;mid=2247483719&amp;idx=1&amp;sn=4f885ef96ce314705e2981170c91c7f9&amp;chksm=fd298977ca5e00613e19d01cbfbc8ee91b8f6f4df28131f2c6e60e275f59d8d641b89be4b3e8#rd" target="_blank" rel="noopener">Android进阶之AIDL的使用详解（一）</a></p><p><a href="https://www.jianshu.com/p/23612b2cce30" target="_blank" rel="noopener">三步掌握 Android 中的 AIDL</a></p><p><a href="https://blog.csdn.net/hahajluzxb/article/details/40544295" target="_blank" rel="noopener">android AIDL在编译中运行的过程</a></p><p><a href="http://www.jianshu.com/p/23612b2cce30" target="_blank" rel="noopener">Android进程通讯——AIDL</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;AIDL是android接口定义语言，可以利用它定义客户端与服务端进行进程间通信 (IPC) 时都认可的编程接口。跨进程通信，不同的进程，运
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="AIDL" scheme="http://yoursite.com/categories/Android/AIDL/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="AIDL" scheme="http://yoursite.com/tags/AIDL/"/>
    
  </entry>
  
  <entry>
    <title>Android中View机制</title>
    <link href="http://yoursite.com/Android-View-Mechanism/"/>
    <id>http://yoursite.com/Android-View-Mechanism/</id>
    <published>2018-01-16T09:38:19.000Z</published>
    <updated>2018-08-07T07:23:03.713Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Android-编程下-Touch-事件的分发和消费机制"><a href="#Android-编程下-Touch-事件的分发和消费机制" class="headerlink" title="Android 编程下 Touch 事件的分发和消费机制"></a>Android 编程下 Touch 事件的分发和消费机制</h1><p>Android 中与 Touch 事件相关的方法包括：dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)；能够响应这些方法的控件包括：ViewGroup、View、Activity。方法与控件的对应关系如下表所示：</p><p>Touch 事件相关方法 方法功能<br>                                                              ViewGroup    View    Activity<br>public boolean dispatchTouchEvent(MotionEvent ev) 事件分发     Yes          Yes      Yes<br>public boolean onInterceptTouchEvent(MotionEvent ev) 事件拦截  Yes          Yes      No<br>public boolean onTouchEvent(MotionEvent ev) 事件响应           Yes          Yes      Yes </p><p>  从这张表中我们可以看到 ViewGroup 和 View 对与 Touch 事件相关的三个方法均能响应，而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。另外需要注意的是 View 对 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的响应的前提是可以向该 View 中添加子 View，如果当前的 View 已经是一个最小的单元 View（比如 TextView），那么就无法向这个最小 View 中添加子 View，也就无法向子 View 进行事件的分发和拦截，所以它没有 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev)，只有 onTouchEvent(MotionEvent ev)。</p><h1 id="Touch-事件分析"><a href="#Touch-事件分析" class="headerlink" title="Touch 事件分析"></a>Touch 事件分析</h1><ol><li><p>事件分发：public boolean dispatchTouchEvent(MotionEvent ev)</p><p>Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式（从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递）将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法，并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下：</p><p>如果 return true，事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费，同时事件会停止向下传递；<br>如果 return false，事件分发分为两种情况：  </p><pre><code>如果当前 View 获取的事件直接来自 Activity，则会将事件返回给 Activity 的 onTouchEvent 进行消费；  如果当前 View 获取的事件来自外层父控件，则会将事件返回给父 View 的 onTouchEvent 进行消费。  </code></pre><p>如果返回系统默认的 super.dispatchTouchEvent(ev)，事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。  </p></li><li><p>事件拦截：public boolean onInterceptTouchEvent(MotionEvent ev)</p><p>在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下，事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下：</p><p>如果 onInterceptTouchEvent 返回 true，则表示将事件进行拦截，并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理；<br>如果 onInterceptTouchEvent 返回 false，则表示将事件放行，当前 View 上的事件会被传递到子 View 上，再由子 View 的 dispatchTouchEvent 来开始这个事件的分发；<br>如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev)，事件默认会被拦截，并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。  </p></li><li><p>事件响应：public boolean onTouchEvent(MotionEvent ev)</p><p>在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下：</p><p>如果事件传递到当前 View 的 onTouchEvent 方法，而该方法返回了 false，那么这个事件会从当前 View 向上传递，并且都是由上层 View 的 onTouchEvent 来接收，如果传递到上面的 onTouchEvent 也返回 false，这个事件就会“消失”，而且接收不到下一次事件。<br>如果返回了 true 则会接收并消费该事件。<br>如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。 </p></li></ol><p>到这里，与 Touch 事件相关的三个方法就分析完毕了。下面的内容会通过各种不同的的测试案例来验证上文中三个方法对事件的处理逻辑。</p><h1 id="Touch-案例介绍"><a href="#Touch-案例介绍" class="headerlink" title="Touch 案例介绍"></a>Touch 案例介绍</h1><p>同样在开始进行案例分析之前，我需要说明测试案例的结构，因为所有的测试都是针对这一个案例来进行的，测试中只是通过修改每个控件中与 Touch 事件相关的三个方法的返回值来体现不同的情况。先来看张图：</p><p>Touch 事件案例布局 UI</p><p>上面的图为测试案例的布局文件 UI 显示效果，布局文件代码如下：</p><h1 id="事件分发机制原理"><a href="#事件分发机制原理" class="headerlink" title="事件分发机制原理"></a>事件分发机制原理</h1><p><a href="http://www.gcssloop.com/customview/dispatch-touchevent-theory" target="_blank" rel="noopener">事件分发机制原理</a>[推荐：★★★★★]</p><h1 id="事件分发机制详解"><a href="#事件分发机制详解" class="headerlink" title="事件分发机制详解"></a>事件分发机制详解</h1><p><a href="http://www.gcssloop.com/customview/dispatch-touchevent-source" target="_blank" rel="noopener">事件分发机制详解</a>[推荐：★★★★★]</p><h1 id="MotionEvent"><a href="#MotionEvent" class="headerlink" title="MotionEvent"></a>MotionEvent</h1><p><a href="https://blog.csdn.net/vansbelove/article/details/78416791" target="_blank" rel="noopener">MotionEvent详解</a>[推荐：★★★★★]</p><h1 id="View-与ViewGroup关系"><a href="#View-与ViewGroup关系" class="headerlink" title="View 与ViewGroup关系"></a>View 与ViewGroup关系</h1><p><a href="http://www.cnblogs.com/hnrainll/archive/2011/11/14/2248564.html" target="_blank" rel="noopener">http://www.cnblogs.com/hnrainll/archive/2011/11/14/2248564.html</a></p><p>.1.0 View及ViewGroup类关系<br>Android View和ViewGroup从组成架构上看，似乎ViewGroup在View之上，View需要继承ViewGroup，但实际上不是这样的。 View是基类，ViewGroup是它的子类。这就证明了一点，View代表了用户界面组件的一块可绘制的空间块。每一个View在屏幕上占据一个长方 形区域。在这个区域内，这个VIEW对象负责图形绘制和事件处理。View是小控件widgets和ViewGroup的父类。ViewGroup又是 Layout的基类。</p><p>image</p><p>image</p><p>从上面两图的对比中，可以看出，实际上ViewGroup是View的子类，因此，View的行为特征ViewGroup也具备，但同时因为 ViewGroup是Layout的祖先，所以具备了其它一些特点,View所未具有的。通常创建一个View，不论是通过XML还是通过代码创建。对任 何一个View及这个View的子类Widget，需要关注如下几个方面：<br>【1】设置属性，如长、宽、着色等。这些属性的设置通常可以用代码实现，也可以用XML文件。并用这些属性在运行时候也可以通常方法进行修改。<br>ID 属性，Android对每个UI元素的ID名称要求唯一，但也不绝对。同时在不同的Layout中是可以相同的元素名称的。给一个UI元素指定ID，有一个好处就是可以在代码中找得到。</p><p>image</p><p>Tags，同ID不同，这个不用来搜索View,类似于对View的一些描述性数据保存。<br>Animation，对任何一个View，可以使用动画对象进行操作。注意，如果View有子的话，子同样具备这个animation功能<br>Position, Size, padding and margins，对任一个View来说，表达这个View通常是宽和高。也可以设置padding和margins。不是所有的View都设置margins。</p><p>image</p><p>Orientatiion，对ViewGroup的子类Layout来说，设置Orienatation，可用来决定子类的位置<br>FillModel，出现这种情况主要是默认情况，某些元素不能完全占满父区域的空间，这时除非子VIEW已经设置具体和DPI，否则话需要告诉父控件，你所选择填充空间方式，如Fill-parent或者Wrap-content等。<br>Gravity，Gravity与Orientation是不同，Gravaity与Word文档中左对齐，右对齐类似。缺省是左上对齐。<br>Weight,这个在两个控制同时分配剩余空间，需要设置layout-weight决定两者谁的占比。<br>【2】请求焦点，可以通过函数实现焦点转换。不同的焦点可以实现不同的背景变换等功能。焦点在Android里分为几种情况，一种是可以获取焦点，另外一种是不能获取焦点，第三种是可获取焦点，但当前正取触摸状态下。<br>【3】设置事件监听者，所有的View都会在本身发生变化将自身的信息广播出去。比喻点击、焦点失去得到等。通常一个事件来到，Android会将事件传 递到相应的View，然后View将事件传递到相应的Listeners。这时View需要获取焦点，如果需要重新绘制View的话，需要调用 invalidate(0或者reqeustLayout重新绘制整个界面。<br>【4】设置显示与隐藏，还可以对其内容设置scrolling。<br>2.1.1 View、Window、Activity、Dispay之间的关系<br>这些都是组成Android 系统显示的关键元素。我们首先来了解Dispay。Dispay代表了硬件显示屏幕信息。</p><p>image</p><p>通过这些函数可以了解一个屏幕的宽、高及分辨率还有是横屏还坚屏等一些基本情况，透过这些函数，我们开发应用时可以方便的得到当前安装我这个应用的屏幕的 大小，以便调整应用使用户得到更好的用户体验。接下来我们看其它三者之间的关系，我想大家虽然看了前面的View的介绍和SDK中关系UI的基本介绍之后 还是对Android图形窗口十分困惑，看API也是，有WindowMangaer接口和Window类，但是在说明文档中，并未提到如何用这些。但实 际上这里面要去看到Android核心，Android核心底层GDI是SKIA，同chrome是一样的GDI，但是GUI是不一样的。这里面 Android实现的是C/S模式。如下图所示</p><p>image</p><p>从上图，我们可以理出大致的显示过程如下：<br>【1】ActivityManagerService创建Activity线程，激活一个activity<br>【2】系统调用Instrumentation.newActivity创建一个activity<br>【3】Activity创建后，attach到一个新创建的phonewindow中。这样Activity获取一个唯一的WindowManager服务的实例<br>【4】Activity创建过程中使用setcontentView设置用用户UI，这些VIEW被加入到PhoneWindow的ContentParent中。<br>【5】Activity线程继续执行，当执行到Activity.makeVisible是将根view DecoView加入到WindowManger中，WindowManger实全会为每个DecoView创建对应的ViewRoot<br>【6】每个ViewRoot拥有一个Surface，每个Surface将会调用底层库创建图形绘制的内存空间。这个底层库就是SurfaceFlinger。SurfaceFlinger同时也负责将个View绘制的图形合到一块（按照Z轴）显示到用户屏幕。<br>【7】如果用户直接在Canvas上绘制，实际上它直接操作Surface。但对每个View的变更，它是要通知到ViewRoot，然后 ViewRoot获取Canvas。如果绘制完成，surfaceFlinger得到通知，合并Surface成一个Surface到设备屏幕。<br>从上面的图形输出过程分析，我们可以知道真正显示图形的实际上跟Activity没有关系，完全由WindowManager来决定。 WindowManager是一个系统服务，因此可以直接调用这个服务来创建界面，并且更绝的是Dialog、Menu也是有WindowManager 来管理的。另外一个我们也可以看到，最底层都是Surface来，因此，常见开发游戏的人都推荐你使用SurfaceView来创建界面。</p><p><img src="/Android-View-Mechanism/01-android-View.jpg" alt="Android中View的机制图"></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://www.jianshu.com/p/9e6c54739217" target="_blank" rel="noopener">ViewRootImpl源码分析事件分发</a><br><a href="https://blog.csdn.net/qian520ao/article/details/80954626" target="_blank" rel="noopener">Android 屏幕刷新机制</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA%3D%3D&amp;mid=2650242156&amp;idx=1&amp;sn=e841a96007b7a0c58fc732d9c03dee6c&amp;scene=45#wechat_redirect" target="_blank" rel="noopener">Android View工作流程</a></p><p><a href="http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html" target="_blank" rel="noopener">Android 编程下 Touch 事件的分发和消费机制</a><br><a href="https://www.cnblogs.com/lucktian/p/6196786.html" target="_blank" rel="noopener">Android View 的事件体系</a> [推荐：★★★★★]<br><a href="http://www.gcssloop.com/customview/dispatch-touchevent-theory" target="_blank" rel="noopener">事件分发机制原理</a>[推荐：★★★★★]<br><a href="http://www.gcssloop.com/customview/dispatch-touchevent-source" target="_blank" rel="noopener">事件分发机制详解</a>[推荐：★★★★★]<br><a href="https://blog.csdn.net/vansbelove/article/details/78416791" target="_blank" rel="noopener">MotionEvent详解</a>[推荐：★★★★★]<br><a href="https://www.cnblogs.com/linjzong/p/4191891.html" target="_blank" rel="noopener">Android:30分钟弄明白Touch事件分发机制</a><br><a href="https://blog.csdn.net/shandian000/article/details/54584782" target="_blank" rel="noopener">Android-View的事件体系</a></p><p><a href="https://blog.csdn.net/woliuyunyicai/article/details/48577617" target="_blank" rel="noopener">View机制深入学习（三） View中的消息传递及InputManagerService</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Android-编程下-Touch-事件的分发和消费机制&quot;&gt;&lt;a href=&quot;#Android-编程下-Touch-事件的分发和消费机制&quot; class=&quot;headerlink&quot; title=&quot;Android 编程下 Touch 事件的分发和消费机制&quot;&gt;&lt;/a&gt;An
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="View" scheme="http://yoursite.com/categories/Android/View/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="View" scheme="http://yoursite.com/tags/View/"/>
    
  </entry>
  
  <entry>
    <title>Android8.1.0 DeskClock横屏滑动冲突</title>
    <link href="http://yoursite.com/Android8-1-0-DeskClock%E6%A8%AA%E5%B1%8F%E6%BB%91%E5%8A%A8%E5%86%B2%E7%AA%81/"/>
    <id>http://yoursite.com/Android8-1-0-DeskClock横屏滑动冲突/</id>
    <published>2018-01-01T09:06:20.000Z</published>
    <updated>2018-08-07T07:24:33.066Z</updated>
    
    <content type="html"><![CDATA[<!---2018-06-20 17:06:20 --><h2 id="问题描述："><a href="#问题描述：" class="headerlink" title="问题描述："></a>问题描述：</h2><p>【Android版本】:Android 8.1.0<br>【预置条件】在时钟界面添加多个不同国家世界时钟<br>【操作步骤】<br> 1.竖屏下滑动查看<br> 2.切换至横屏滑动查看<br>【实际结果】<br>  2.横屏下出现滑动条不动，时钟界面自己在不停的更新，见视频<br>【期望结果】<br> 横屏下可正常滑动查看</p><h2 id="问题分析："><a href="#问题分析：" class="headerlink" title="问题分析："></a>问题分析：</h2><p><strong> 1）</strong> 找到滑动的View的布局代码在<br>竖屏是布局：packages/apps/DeskClock/res/layout/clock_fragment.xml<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;android.support.v7.widget.RecyclerView</span><br><span class="line">    xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    android:id=<span class="string">"@+id/cities"</span></span><br><span class="line">    android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_weight=<span class="string">"1"</span></span><br><span class="line">    android:clickable=<span class="string">"false"</span></span><br><span class="line">    android:clipToPadding=<span class="string">"false"</span></span><br><span class="line">    android:paddingBottom=<span class="string">"@dimen/fab_height"</span></span><br><span class="line">    android:scrollbarStyle=<span class="string">"outsideOverlay"</span></span><br><span class="line">    android:scrollbars=<span class="string">"vertical"</span> /&gt;</span><br></pre></td></tr></table></figure></p><p>横屏的布局：packages/apps/DeskClock/res/layout-land/clock_fragment.xml<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">&lt;LinearLayout</span><br><span class="line">    xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></span><br><span class="line">    android:layout_width=<span class="string">"match_parent"</span></span><br><span class="line">    android:layout_height=<span class="string">"match_parent"</span>&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- Left gutter. --&gt;</span><br><span class="line">    &lt;Space</span><br><span class="line">        android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">        android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">        android:layout_weight=<span class="string">"@integer/gutter_width_percent"</span> /&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- Clock: 62% of total width (4% given to right gutter). --&gt;</span><br><span class="line">    &lt;LinearLayout</span><br><span class="line">        android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">        android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">        android:layout_weight=<span class="string">"62"</span></span><br><span class="line">        android:gravity=<span class="string">"center"</span></span><br><span class="line">        android:paddingBottom=<span class="string">"@dimen/fab_height"</span>&gt;</span><br><span class="line"></span><br><span class="line">        &lt;include</span><br><span class="line">            android:id=<span class="string">"@+id/main_clock_left_pane"</span></span><br><span class="line">            layout=<span class="string">"@layout/main_clock_frame"</span></span><br><span class="line">            android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">            android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line">            android:layout_weight=<span class="string">"29"</span> /&gt;</span><br><span class="line"></span><br><span class="line">        &lt;!-- Right gutter. --&gt;</span><br><span class="line">        &lt;Space</span><br><span class="line">            android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">            android:layout_height=<span class="string">"match_parent"</span></span><br><span class="line">            android:layout_weight=<span class="string">"2"</span> /&gt;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &lt;/LinearLayout&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- World Clock List: 33% of total width. Right gutter is applied <span class="keyword">in</span> world_clock_item. --&gt;</span><br><span class="line">    &lt;android.support.v7.widget.RecyclerView</span><br><span class="line">        android:id=<span class="string">"@+id/cities"</span></span><br><span class="line">        android:layout_width=<span class="string">"0dp"</span></span><br><span class="line">        android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line">        android:layout_gravity=<span class="string">"center"</span></span><br><span class="line">        android:layout_weight=<span class="string">"33"</span></span><br><span class="line">        android:clickable=<span class="string">"false"</span></span><br><span class="line">        android:clipToPadding=<span class="string">"false"</span></span><br><span class="line">        android:paddingBottom=<span class="string">"@dimen/fab_height"</span></span><br><span class="line">        android:paddingTop=<span class="string">"16dp"</span></span><br><span class="line">        android:scrollbarStyle=<span class="string">"outsideOverlay"</span></span><br><span class="line">        android:scrollbars=<span class="string">"vertical"</span> /&gt;</span><br><span class="line"></span><br><span class="line">&lt;/LinearLayout&gt;</span><br></pre></td></tr></table></figure></p><p>Java代码实现滑动：packages/apps/DeskClock/src/com/android/deskclock/ClockFragment.java</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;!---2018-06-20 17:06:20 --&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;【Android版本】:Android 8.1.0&lt;br&gt;
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android DeskClock View" scheme="http://yoursite.com/tags/Android-DeskClock-View/"/>
    
  </entry>
  
  <entry>
    <title>Android好文</title>
    <link href="http://yoursite.com/Android-Article/"/>
    <id>http://yoursite.com/Android-Article/</id>
    <published>2017-12-11T16:00:00.000Z</published>
    <updated>2018-08-07T07:12:56.142Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://blog.csdn.net/melodev/article/details/51235427" target="_blank" rel="noopener">Android开发者必须阅读的优秀学习资源</a><br>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="重点关注"><a href="#重点关注" class="headerlink" title="重点关注"></a>重点关注</h1><p><a href="https://blog.csdn.net/innost" target="_blank" rel="noopener">深入理解Android卷III相关</a></p><h1 id="杂"><a href="#杂" class="headerlink" title="杂"></a>杂</h1><p>看过的比较好的文章：<br><a href="https://www.jianshu.com/p/a4b8e4c5d9b0" target="_blank" rel="noopener">Android 目前最稳定和高效的UI适配方案</a><br><a href="https://xudeveloper.github.io/2018/06/18/Android%20AccessibilityService%E6%9C%BA%E5%88%B6%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/" target="_blank" rel="noopener">Android AccessibilityService机制源码解析</a><br><a href="https://droidyue.com/blog/2014/11/08/understanding-garbage-collection-output-messages-in-android/" target="_blank" rel="noopener">译文：理解Android中垃圾回收日志信息</a><br><a href="http://www.importnew.com/13107.html" target="_blank" rel="noopener">从Java代码到字节码（1）</a><br><a href="http://www.importnew.com/29076.html" target="_blank" rel="noopener">细说 Java 中的字符和字符串（ 二 ）</a><br><a href="https://snowdream86.gitbooks.io/github-cheat-sheet/content/zh/index.html" target="_blank" rel="noopener">GitHub秘籍</a><br><a href="https://blog.csdn.net/androidsecurity/article/details/41207959" target="_blank" rel="noopener">“黑暗潜伏者” – 手机病毒新型攻击方式</a><br><a href="http://blog.jobbole.com/30940/" target="_blank" rel="noopener">缓存、缓存算法和缓存框架简介</a><br><a href="http://duanqz.github.io/2015-08-30-Intro-to-automerger" target="_blank" rel="noopener">AOSP代码管理</a><br><a href="https://blog.csdn.net/t12x3456/article/details/39958755" target="_blank" rel="noopener">Android 使用动态加载框架DL进行插件化开发</a><br><a href="https://blog.csdn.net/innost" target="_blank" rel="noopener">深入理解Android卷III相关</a><br><a href="https://blog.csdn.net/qq_23547831/article/details/51487978" target="_blank" rel="noopener">Android源码解析（二十八）–&gt;电源开关机按键事件流程</a>  有很多的流程分析的文章</p><h1 id="优秀的网站"><a href="#优秀的网站" class="headerlink" title="优秀的网站"></a>优秀的网站</h1><p><a href="https://github.com/AweiLoveAndroid/CommonDevKnowledge" target="_blank" rel="noopener">常用的Android开发的一些技能点汇集</a><br><a href="https://github.com/DanluTeam/ColdStart" target="_blank" rel="noopener">awesome-android-performance</a> 性能优化<br><a href="https://github.com/EricChows/free-programming-books/blob/master/free-programming-books-zh.md" target="_blank" rel="noopener">免费的编程书籍</a><br><a href="https://github.com/EricChows/Worth-Reading-the-Android-technical-articles" target="_blank" rel="noopener">Android优质资源合集</a><br><a href="http://wiki.jikexueyuan.com/project/deep-android-v2/activity.html" target="_blank" rel="noopener">极客学院-深入理解 Android 卷II</a><br>有SystemServer/PackageManagerService/PowerManagerService/ActivityManagerService/ContentProvider等讲解<br><a href="http://top.jobbole.com/17733/" target="_blank" rel="noopener">每个程序员至少阅读两次的 10 篇技术论文</a></p><h1 id="优秀的期刊"><a href="#优秀的期刊" class="headerlink" title="优秀的期刊"></a>优秀的期刊</h1><p><a href="https://www.androidweekly.cn/" target="_blank" rel="noopener">Android开发技术周刊</a><br><a href="http://www.androidblog.cn/" target="_blank" rel="noopener">Android博客周刊</a></p><h1 id="重点阅读文章"><a href="#重点阅读文章" class="headerlink" title="重点阅读文章"></a>重点阅读文章</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">罗升阳–那两年炼就的Android内功修养</a><br><a href="https://github.com/LRH1993/android_interview" target="_blank" rel="noopener">Android校招面试指南</a><br><a href="https://github.com/helen-x/AndroidInterview" target="_blank" rel="noopener">Android面试资料</a><br><a href="https://github.com/open-android/Android" target="_blank" rel="noopener">阅读里面的Android面试题</a><br><a href="https://github.com/tangqi92/Android-Tips" target="_blank" rel="noopener">Android踩坑历史</a><br><a href="https://github.com/lizhangqu/CoreLink" target="_blank" rel="noopener">Android 日常积累</a><br><a href="https://github.com/stormzhang/android-interview-questions-cn" target="_blank" rel="noopener">Android面试指南</a><br><a href="https://github.com/Juude/droidReverse" target="_blank" rel="noopener">Android一些重要知识点整理</a><br><a href="https://github.com/AweiLoveAndroid/CommonDevKnowledge" target="_blank" rel="noopener">常用的Android开发的一些技能点汇集</a></p><p><a href="https://blog.csdn.net/china_style/article/details/79280137" target="_blank" rel="noopener">Android 8.0后台运行策略学习</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzIwMzYwMTk1NA==&amp;mid=2247489819&amp;idx=1&amp;sn=a209e3ab126499cb91d6a79ae4cc1fe3&amp;chksm=96cdb856a1ba31408305784a58ec6333833efa28e66ddb5ded160496ff667c824a71075b27a9#rd" target="_blank" rel="noopener">Android面试大纲（集合）</a><br><a href="https://www.kancloud.cn/alex_wsc/androids/477708" target="_blank" rel="noopener">老罗的Android之旅（总结）</a></p><h1 id="优秀的文章"><a href="#优秀的文章" class="headerlink" title="优秀的文章"></a>优秀的文章</h1><p><a href="https://github.com/AweiLoveAndroid/CommonDevKnowledge/blob/master/github_README/README%E6%96%87%E6%A1%A3%E7%9A%84%E8%A7%84%E8%8C%83%E5%86%99%E6%B3%95.md" target="_blank" rel="noopener">README文档的规范写法</a><br><a href="https://github.com/AweiLoveAndroid/CommonDevKnowledge/blob/master/interview/summary.md" target="_blank" rel="noopener">国内一线互联网公司面试题汇总</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzIyMjQ0MTU0NA==&amp;mid=2247484617&amp;idx=1&amp;sn=3734e643d241ac9615424dd44462ee2d&amp;chksm=e82c3deedf5bb4f82e7be0823739774a0a2cf8372284c8409471c2752fea1f367ca3f6857795&amp;mpshare=1&amp;scene=23&amp;srcid=1128DKotEvTe4dheaTextbqp#rd" target="_blank" rel="noopener">阿里、腾讯、百度、华为、京东、搜狗和滴滴最新面试题汇集</a><br><a href="https://zhuanlan.zhihu.com/p/30016683" target="_blank" rel="noopener">2017下半年，一二线互联网公司Android面试题汇总</a><br><a href="https://zhuanlan.zhihu.com/p/26327485" target="_blank" rel="noopener">2017 年初、阿里、腾讯、百度、华为、京东、搜狗和滴滴面试题汇集（更新篇）</a>  带答案<br><a href="https://github.com/LRH1993/android_interview" target="_blank" rel="noopener">Android校招面试指南</a><br><a href="http://www.jackywang.tech/AndroidInterview-Q-A/" target="_blank" rel="noopener">The top Internet companies android interview questions and answers</a><br><a href="https://crossoverjie.top/2018/06/21/personal/Interview-experience/" target="_blank" rel="noopener">一个学渣的阿里之路</a><br><a href="https://github.com/DanluTeam/ColdStart" target="_blank" rel="noopener">App 启动优化</a><br><a href="http://zmywly8866.github.io/2016/05/04/android-application-leak-analysis-and-fix.html" target="_blank" rel="noopener">Android应用内存泄露分析、改善经验总结</a><br><a href="http://wiki.jikexueyuan.com/project/deep-android-v2/packagemanagerservice.html" target="_blank" rel="noopener">第4章  深入理解 PackageManagerService</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">罗升阳–那两年炼就的Android内功修养</a> 老罗博客的总纲<br>需要把 <a href="https://github.com/EricChows/Worth-Reading-the-Android-technical-articles" target="_blank" rel="noopener">https://github.com/EricChows/Worth-Reading-the-Android-technical-articles</a> 这里面的优秀文章一篇一篇的看完。<br><a href="https://github.com/helen-x/AndroidInterview" target="_blank" rel="noopener">Android面试资料</a></p><h1 id="Android开发工具"><a href="#Android开发工具" class="headerlink" title="Android开发工具"></a>Android开发工具</h1><p><a href="http://www.androiddevtools.cn/" target="_blank" rel="noopener">AndroidDevTools</a><br><a href="https://github.com/inferjay/AndroidDevTools/" target="_blank" rel="noopener">AndroidDevTools(Github)</a><br><a href="http://androidblog.cn/tools/" target="_blank" rel="noopener">Android开发工具汇总</a><br><a href="http://editor.method.ac" target="_blank" rel="noopener">Method draw 矢量图 在线查看工具</a></p><h1 id="Android官方文档-中文版"><a href="#Android官方文档-中文版" class="headerlink" title="Android官方文档 中文版"></a>Android官方文档 中文版</h1><p><a href="http://hukai.me/android-training-course-in-chinese/index.html" target="_blank" rel="noopener">AndroidTraning中文版</a></p><h1 id="Android-精选资源"><a href="#Android-精选资源" class="headerlink" title="Android 精选资源"></a>Android 精选资源</h1><p><a href="https://github.com/android-cn/android-open-project-analysis" target="_blank" rel="noopener">Android 开源项目源码解析</a><br><a href="https://github.com/amitshekhariitbhu/awesome-android-complete-reference" target="_blank" rel="noopener">awesome-android-complete-reference</a><br><a href="https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis" target="_blank" rel="noopener">Android源码解析</a> 一帮Android痴迷者，自己对Android源码进行解析，并共享<br><a href="https://github.com/zmywly8866/Worth-Reading-the-Android-technical-articles" target="_blank" rel="noopener">Android值得阅读的技术文章</a></p><p><strong> 免费的编程中文书籍索引 </strong><br><a href="http://siberiawolf.com/free_programming/index.html?utm_source=androidweekly.io&amp;utm_medium=website" target="_blank" rel="noopener">http://siberiawolf.com/free_programming/index.html?utm_source=androidweekly.io&amp;utm_medium=website</a><br><a href="https://github.com/EricChows/free-programming-books/blob/master/free-programming-books-zh.md" target="_blank" rel="noopener">https://github.com/EricChows/free-programming-books/blob/master/free-programming-books-zh.md</a></p><h1 id="Android-性能相关"><a href="#Android-性能相关" class="headerlink" title="Android 性能相关"></a>Android 性能相关</h1><p><a href="https://mp.weixin.qq.com/s/A8NZbHlU1J2CsoNhTFX8Dg?utm_source=androidweekly.io&amp;utm_medium=website" target="_blank" rel="noopener">Android性能优化来龙去脉总结</a><br>总结： 大概的讲述了性能优化的类型和方式<br><a href="https://github.com/DanluTeam/ColdStart?utm_source=androidweekly.io&amp;utm_medium=website" target="_blank" rel="noopener">App 启动优化</a><br>总结：<br><a href="https://github.com/Juude/awesome-android-performance" target="_blank" rel="noopener">Android性能优化视频，文档以及工具</a><br><a href="https://blog.csdn.net/innost/article/details/9008691" target="_blank" rel="noopener">Android系统性能调优工具介绍</a><br><a href="http://blog.jobbole.com/78995" target="_blank" rel="noopener">正确使用Android性能分析工具——TraceView</a><br><a href="http://www.codeceo.com/article/android-memory-manage.html" target="_blank" rel="noopener">每个Android开发者必须知道的内存管理知识</a></p><h1 id="Android-内存相关"><a href="#Android-内存相关" class="headerlink" title="Android 内存相关"></a>Android 内存相关</h1><p><a href="http://www.cnblogs.com/kingOfPointer/archive/2012/12/21/2828018.html" target="_blank" rel="noopener">android 开发如何做内存优化</a><br><a href="https://www.cnblogs.com/ThinkVenus/p/6805495.html" target="_blank" rel="noopener">什么是java OOM？如何分析及解决oom问题？</a><br><a href="https://www.cnblogs.com/kongzhongqijing/articles/7283599.html" target="_blank" rel="noopener">常见OOM现象</a><br><a href="https://blog.csdn.net/a396901990/article/details/38904543" target="_blank" rel="noopener">ANDROID内存优化(大汇总——全)</a><br><a href="http://blog.csdn.net/imain/article/details/8560986" target="_blank" rel="noopener">Android内存优化</a><br><a href="http://blog.csdn.net/awangyunke/article/details/20380719" target="_blank" rel="noopener">Android 内存优化</a><br><a href="http://www.cnblogs.com/zyw-205520/archive/2013/02/17/2914190.htm" target="_blank" rel="noopener">关于android性能，内存优化</a><br><a href="http://www.360doc.com/content/11/0911/15/18042_147476260.shtml" target="_blank" rel="noopener">Java垃圾回收原理</a><br><a href="http://www.360doc.com/content/11/0911/16/18042_147492404.shtml" target="_blank" rel="noopener">JVM垃圾回收（GC）原理</a></p><h1 id="Android-稳定性相关"><a href="#Android-稳定性相关" class="headerlink" title="Android 稳定性相关"></a>Android 稳定性相关</h1><h1 id="Android-功耗相关"><a href="#Android-功耗相关" class="headerlink" title="Android 功耗相关"></a>Android 功耗相关</h1><h1 id="Android-Flutter-相关"><a href="#Android-Flutter-相关" class="headerlink" title="Android Flutter 相关"></a>Android Flutter 相关</h1><p><a href="https://mp.weixin.qq.com/s/vlHt8jxbdzBqJZDobpsFVw?utm_source=androidweekly.io&amp;utm_medium=website" target="_blank" rel="noopener">深入理解flutter的编译原理与优化</a></p><h1 id="Android-虚拟机相关"><a href="#Android-虚拟机相关" class="headerlink" title="Android 虚拟机相关"></a>Android 虚拟机相关</h1><h1 id="Android-AIDL-相关"><a href="#Android-AIDL-相关" class="headerlink" title="Android AIDL 相关"></a>Android AIDL 相关</h1><h1 id="Android-升级相关"><a href="#Android-升级相关" class="headerlink" title="Android 升级相关"></a>Android 升级相关</h1><h1 id="Android-Java-基础"><a href="#Android-Java-基础" class="headerlink" title="Android/Java 基础"></a>Android/Java 基础</h1><h1 id="线程、多线程和线程池"><a href="#线程、多线程和线程池" class="headerlink" title="线程、多线程和线程池"></a>线程、多线程和线程池</h1><h1 id="并发编程"><a href="#并发编程" class="headerlink" title="并发编程"></a>并发编程</h1><p>  Android方面，并发编程用的比较少，需要好好的研究一下</p><h1 id="数据结构相关"><a href="#数据结构相关" class="headerlink" title="数据结构相关"></a>数据结构相关</h1><h1 id="工具使用："><a href="#工具使用：" class="headerlink" title="工具使用："></a>工具使用：</h1><p><a href="https://blog.csdn.net/maosidiaoxian/article/details/44992655" target="_blank" rel="noopener">打造你的开发神器——介绍Android Studio上的几个插件</a><br><a href="https://blog.csdn.net/maosidiaoxian/article/category/2380095" target="_blank" rel="noopener">Android 官方技术文档翻译</a><br><a href="http://duanqz.github.io/2015-05-21-Intro-adb" target="_blank" rel="noopener">adb 介绍</a><br><a href="http://duanqz.github.io/2015-07-19-Intro-to-dumpsys" target="_blank" rel="noopener">dumpsys 介绍</a><br><a href="http://duanqz.github.io/2015-06-25-Intro-to-Repo" target="_blank" rel="noopener">repo 介绍</a><br><a href="http://duanqz.github.io/2016-01-06-Intro-Google-Mobile-Service" target="_blank" rel="noopener">GMS 介绍</a></p><h1 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h1><p><a href="https://mp.weixin.qq.com/s/cXa6Rwiet5gN8bSe-h4QNw" target="_blank" rel="noopener">通过Google挖掘细分市场的一个案例</a></p><p><a href="https://36kr.com/p/5107620.html" target="_blank" rel="noopener">Facebook PM谈顶级产品经理具备的4个特征</a></p><p><a href="https://mp.weixin.qq.com/s/wqGgaH6EhSOqjj1SyJ4U7w" target="_blank" rel="noopener">陈春花：具有战略思维而非竞争理念</a></p><p><a href="https://arstechnica.com/gadgets/2018/07/googles-iron-grip-on-android-controlling-open-source-by-any-means-necessary/" target="_blank" rel="noopener">Google的霸道</a><br><a href="https://www.toutiao.com/a6585814244938220045/" target="_blank" rel="noopener">Google的霸道：我就是要独享安卓源代码！</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/melodev/article/details/51235427&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Android开发者必须阅读的优秀学习资源&lt;/a&gt;&lt;br&gt;Ongoing,待完成，
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Article" scheme="http://yoursite.com/categories/Android/Article/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Article" scheme="http://yoursite.com/tags/Article/"/>
    
  </entry>
  
  <entry>
    <title>Java基础知识汇总</title>
    <link href="http://yoursite.com/Java-Base/"/>
    <id>http://yoursite.com/Java-Base/</id>
    <published>2017-07-16T09:50:38.000Z</published>
    <updated>2018-08-28T08:09:02.898Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Java-基础"><a href="#Java-基础" class="headerlink" title="Java 基础"></a>Java 基础</h1><p><a href="http://www.runoob.com/java/java-tutorial.html" target="_blank" rel="noopener">Java基础教程</a></p><p><a href="https://blog.csdn.net/shuhaojie/article/details/78236816" target="_blank" rel="noopener">Java基础知识入门</a><br><a href="https://www.cnblogs.com/javastu/p/5519569.html" target="_blank" rel="noopener">Java基础总结大全（实用）</a></p><h1 id="JDK"><a href="#JDK" class="headerlink" title="JDK"></a>JDK</h1><p><a href="http://tool.oschina.net/apidocs/apidoc?api=jdk-zh" target="_blank" rel="noopener">JDK文档</a></p><h1 id="Java-源码解析"><a href="#Java-源码解析" class="headerlink" title="Java 源码解析"></a>Java 源码解析</h1><p>源码解析类网址：<br><a href="https://blog.csdn.net/benjaminlee1/article/category/6477755" target="_blank" rel="noopener">https://blog.csdn.net/benjaminlee1/article/category/6477755</a></p><p><a href="https://blog.csdn.net/benjaminlee1/article/details/72843713" target="_blank" rel="noopener">Java-Object类源码解析</a></p><h1 id="Java-反射机制"><a href="#Java-反射机制" class="headerlink" title="Java 反射机制"></a>Java 反射机制</h1><p><a href="https://blog.csdn.net/u011240877/article/details/54604212/" target="_blank" rel="noopener">深入理解 Java 反射：Field （成员变量）</a></p><h1 id="JVM"><a href="#JVM" class="headerlink" title="JVM"></a>JVM</h1><p>★<a href="../Java-VirtualMachine">Java虚拟机</a></p><p>JVM相关网址：<br><a href="https://blog.csdn.net/benjaminlee1/article/category/6483976" target="_blank" rel="noopener">https://blog.csdn.net/benjaminlee1/article/category/6483976</a></p><p><a href="https://www.cnblogs.com/dingyingsi/p/3760447.html" target="_blank" rel="noopener">深入理解JVM—JVM内存模型</a></p><h1 id="Dalvik"><a href="#Dalvik" class="headerlink" title="Dalvik"></a>Dalvik</h1><h1 id="ART"><a href="#ART" class="headerlink" title="ART"></a>ART</h1><h1 id="LLVM"><a href="#LLVM" class="headerlink" title="LLVM"></a>LLVM</h1><p>待后续补充</p><!--  [Java面试题](../Android-InterView)  --><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Java-基础&quot;&gt;&lt;a href=&quot;#Java-基础&quot; class=&quot;headerlink&quot; title=&quot;Java 基础&quot;&gt;&lt;/a&gt;Java 基础&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;http://www.runoob.com/java/java-tutorial.
      
    
    </summary>
    
      <category term="Java" scheme="http://yoursite.com/categories/Java/"/>
    
      <category term="Base" scheme="http://yoursite.com/categories/Java/Base/"/>
    
    
      <category term="Base" scheme="http://yoursite.com/tags/Base/"/>
    
      <category term="Java" scheme="http://yoursite.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>Java-VirtualMachine</title>
    <link href="http://yoursite.com/Java-VirtualMachine/"/>
    <id>http://yoursite.com/Java-VirtualMachine/</id>
    <published>2017-07-16T09:42:53.000Z</published>
    <updated>2018-08-07T07:48:04.149Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Java中堆内存和栈内存详解"><a href="#Java中堆内存和栈内存详解" class="headerlink" title="Java中堆内存和栈内存详解"></a>Java中堆内存和栈内存详解</h1><p><a href="http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html" target="_blank" rel="noopener">http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html</a></p><p>Java把内存分成两种，一种叫做栈内存，一种叫做堆内存</p><p>在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时，java就在栈中为这个变量分配内存空间，当超过变量的作用域后，java会自动释放掉为该变量分配的内存空间，该内存空间可以立刻被另作他用。<br>堆内存用于存放由new创建的对象和数组。在堆中分配的内存，由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后，还可以在栈中定义一个特殊的变量，这个变量的取值等于数组或者对象在堆内存中的首地址，在栈中的这个特殊的变量就变成了数组或者对象的引用变量，以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象，引用变量相当于为数组或者对象起的一个别名，或者代号。<br>引用变量是普通变量，定义时在栈中分配内存，引用变量在程序运行到作用域外释放。而数组＆对象本身在堆中分配，即使程序运行到使用new产生数组和对象的语句所在地代码块之外，数组和对象本身占用的堆内存也不会被释放，数组和对象在没有引用变量指向它的时候，才变成垃圾，不能再被使用，但是仍然占着内存，在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因，实际上，栈中的变量指向堆内存中的变量，这就是 Java 中的指针!</p><p>java中内存分配策略及堆和栈的比较</p><p>　　1 内存分配策略</p><p>　　按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.</p><p>　　静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.</p><p>　　栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。</p><p>　　静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.</p><p>　　2 堆和栈的比较</p><p>　　上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈:</p><p>　　从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的，栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:</p><p>　　在编程中，例如C/C++中，所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候，修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个”大小多少”是在编译时确定的,不是在运行时.</p><p>　　堆是应用程序在运行的时候请求操作系统分配给自己内存，由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间，因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间，也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中，要求创建一个对象时，只需用 new命令编制相关的代码即可。执行这些代码时，会在堆里自动进行数据的保存.当然，为达到这种灵活性，必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~).</p><p>　　3 JVM中的堆和栈</p><p>　　JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说，它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。</p><p>　　我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.</p><p>　　从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域，该区域具有先进后出的特性。</p><p>　　每一个Java应用都唯一对应一个JVM实例，每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同，Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的，但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存，在堆中分配的内存实际建立这个对象，而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。</p><p>　　Java 中的堆和栈</p><p>　　Java把内存划分成两种：一种是栈内存，一种是堆内存。</p><p>　　在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。</p><p>　　当在一段代码块定义一个变量时，Java就在栈中为这个变量分配内存空间，当超过变量的作用域后，Java会自动释放掉为该变量所分配的内存空间，该内存空间可以立即被另作他用。</p><p>　　堆内存用来存放由new创建的对象和数组。</p><p>　　在堆中分配的内存，由Java虚拟机的自动垃圾回收器来管理。</p><p>　　在堆中产生了一个数组或对象后，还可以在栈中定义一个特殊的变量，让栈中这个变量的取值等于数组或对象在堆内存中的首地址，栈中的这个变量就成了数组或对象的引用变量。</p><p>　　引用变量就相当于是为数组或对象起的一个名称，以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。</p><p>　　具体的说：</p><p>　　栈与堆都是Java用来在Ram中存放数据的地方。与C++不同，Java自动管理栈和堆，程序员不能直接地设置栈或堆。</p><p>　　Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立，它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的，堆的优势是可以动态地分配内存大小，生存期也不必事先告诉编译器，因为它是在运行时动态分配内存的，Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是，由于要在运行时动态分配内存，存取速度较慢。</p><p>　　栈的优势是，存取速度比堆要快，仅次于寄存器，栈数据可以共享。但缺点是，存在栈中的数据大小与生存期必须是确定的，缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。</p><p>　　栈有一个很重要的特殊性，就是存在栈中的数据可以共享。假设我们同时定义：</p><p>　　int a = 3;</p><p>　　int b = 3;</p><p>　　编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用，然后查找栈中是否有3这个值，如果没找到，就将3存放进来，然后将a指向3。接着处理int b = 3;在创建完b的引用变量后，因为在栈中已经有3这个值，便将b直接指向3。这样，就出现了a与b同时均指向3的情况。这时，如果再令a=4;那么编译器会重新搜索栈中是否有4值，如果没有，则将4存放进来，并令a指向4;如果已经有了，则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的，因为这种情况a的修改并不会影响到b, 它是由编译器完成的，它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态，会影响到另一个对象引用变量</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://www.jianshu.com/p/ebde5a8fd767" target="_blank" rel="noopener">《深入理解Java虚拟机》笔记之类文件结构与类加载机制</a><br><a href="https://www.jianshu.com/p/4ee763f91ce7" target="_blank" rel="noopener">《深入理解Java虚拟机》笔记之JAVA内存模式与垃圾回收</a></p><p><a href="https://blog.csdn.net/luoshengyang/article/details/41581063" target="_blank" rel="noopener">Dalvik虚拟机Java堆创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/41822747" target="_blank" rel="noopener">Dalvik虚拟机垃圾收集（GC）过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/42072975" target="_blank" rel="noopener">ART运行时垃圾收集机制简要介绍和学习计划</a></p><p><a href="https://github.com/chinageek/jvm" target="_blank" rel="noopener">《JVM必知必会》</a><br><a href="https://github.com/JeffLi1993/jvm-core-learning-example" target="_blank" rel="noopener">JVM 学习的例子</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Java中堆内存和栈内存详解&quot;&gt;&lt;a href=&quot;#Java中堆内存和栈内存详解&quot; class=&quot;headerlink&quot; title=&quot;Java中堆内存和栈内存详解&quot;&gt;&lt;/a&gt;Java中堆内存和栈内存详解&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;http://www.cn
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Ubuntu 配置 OpenGork 服务</title>
    <link href="http://yoursite.com/Ubuntu-OpenGork/"/>
    <id>http://yoursite.com/Ubuntu-OpenGork/</id>
    <published>2017-05-31T09:38:08.000Z</published>
    <updated>2018-08-07T07:57:04.288Z</updated>
    
    <content type="html"><![CDATA[<h1 id="OpenGork的配置"><a href="#OpenGork的配置" class="headerlink" title="OpenGork的配置"></a>OpenGork的配置</h1><p>OpenGork的好处，这里不做介绍了，可以参考<a href="https://zhuanlan.zhihu.com/p/24369747" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/24369747</a><br>OpenGork是用Java实现的，源码见 <a href="https://github.com/oracle/opengrok/" target="_blank" rel="noopener">https://github.com/oracle/opengrok/</a><br>OpenGork官网：<a href="http://oracle.github.io/opengrok/" target="_blank" rel="noopener">http://oracle.github.io/opengrok/</a><br>OpenGork下载地址：<a href="https://github.com/oracle/opengrok/releases" target="_blank" rel="noopener">https://github.com/oracle/opengrok/releases</a> 这里可以下载编译好的文件，或者下载源码</p><p>配置OpenGork之前需要一些其他的环境：  </p><ol><li>Java环境：JDK 以及配置好环境变量<br>—如何实现请自行查找  </li><li>Tomcat 服务：需要下载Tomcat服务<br>下载地址：<a href="http://tomcat.apache.org/" target="_blank" rel="noopener">http://tomcat.apache.org/</a><br><strong>注意一：Tomcat7.0版本和JDK1.8版本无法兼容</strong>  </li><li>OpenGork：<a href="https://github.com/oracle/opengrok/releases" target="_blank" rel="noopener">https://github.com/oracle/opengrok/releases</a>  </li><li>遗漏了ctags，如果这个工具没有安装和在Opengork里面配置的话，就算Tomcat和Opengork都配置好了，且都能正常运行，还是无法进行搜索，而且Opengork网站里面也无法打开文件。根本原因是ctags 就是用来建立索引的工具，没了它，后面的搜索就无从说起。<br>Ubuntu 安装ctags命令:sudo apt-get install ctags<br>Ubuntu自带了exuberant ctags,在后面的OpenGork的配置文件中，需要FindExuberanCtags()函数的binary中需要填写为binay=”exuberant ctags”</li></ol><p><strong>注意二：Tomcat 和 Jdk不要直接用sudo apt-get install的命令方式来安装。这样做了，后面改配置文件会很痛苦</strong></p><p>前期准备工作做好了之后，下面来进行安装和配置：  </p><ol><li><p>安装Tomcat<br>1)解压下载下来的Tomcat压缩包<br>进入Tomcat解压目录/bin 下执行sudo ./startup.sh<br>启动Tomcat<br>2).在浏览器输入<a href="http://localhost:8080" target="_blank" rel="noopener">http://localhost:8080</a> 或者192.168.0.1:8080<br>局域网内其他机器可以输入<a href="http://ip:8080" target="_blank" rel="noopener">http://ip:8080</a>来测试Tomcat服务是否启动成功</p></li><li><p>安装OpenGork<br>1)在/opt目录下新建opengork目录<br>2)将下载下来的opengork压缩包解压到/opt/opengork目录下<br>3)opengrok/lib目录下的source.war包拷贝到apache-tomcat/webapps目录下<br>4)在浏览器输入<a href="http://localhost:8080/source" target="_blank" rel="noopener">http://localhost:8080/source</a><br>5)能正常看到opengork起来了的界面就Ok了。<br>如果Tomcat版本是7.0，JDK版本是1.8的话，或在这个界面报各种http 400/500等各种乱七八糟的错误</p></li><li><p>配置OpenGork<br>在/opt/opengrok目录的bin文件下 vim OpenGork文件<br>找到DefaultInstanceConfiguration函数里面的<br>OPENGROK_INSTANCE_BASE=”${OPENGROK_INSTANCE_BASE:-/var/opengrok}”</p></li></ol><p>这一块<a href="https://blog.csdn.net/tyronne/article/details/34984805" target="_blank" rel="noopener">https://blog.csdn.net/tyronne/article/details/34984805</a> 这篇文章写的蛮好，可以参考</p><ol start="4"><li>客制化Opengork<br>由于Opengork的代码需要建立索引，一般是在/opt/opengork/有src data等目录<br>其实可以自己克制化这个目录<br>比如：在/home/xxx/Code/目录下新建一个opengork目录<br>在里面再建好src data etc三个目录</li></ol><p>在第三步的/opt/opengork/bin/OpenGork这个配置文件里面和Tomcat的web.xml文件里面<br>把路径都配置成/home/xxx/Code/opengork就可以了</p><p>后面可以把代码放在/home/xxx/Code/src目录下面</p><h1 id="Android源码-OpenGork"><a href="#Android源码-OpenGork" class="headerlink" title="Android源码 OpenGork"></a>Android源码 OpenGork</h1><p>以下三个OpenGork都已经部署好，可以查询从Android1.6到最新版本的Android 以及Kernel代码</p><p><a href="http://androidxref.com/" target="_blank" rel="noopener">androidxref</a><br><a href="https://www.androidos.net.cn/" target="_blank" rel="noopener">androidos</a><br><a href="http://android.macpod.net/" target="_blank" rel="noopener">macpod</a></p><p>这三个网站各有各自的优点</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;OpenGork的配置&quot;&gt;&lt;a href=&quot;#OpenGork的配置&quot; class=&quot;headerlink&quot; title=&quot;OpenGork的配置&quot;&gt;&lt;/a&gt;OpenGork的配置&lt;/h1&gt;&lt;p&gt;OpenGork的好处，这里不做介绍了，可以参考&lt;a href=&quot;h
      
    
    </summary>
    
      <category term="Tools" scheme="http://yoursite.com/categories/Tools/"/>
    
    
  </entry>
  
  <entry>
    <title>IT学习方法论</title>
    <link href="http://yoursite.com/IT-Base/"/>
    <id>http://yoursite.com/IT-Base/</id>
    <published>2017-05-04T16:00:00.000Z</published>
    <updated>2018-08-10T03:55:04.002Z</updated>
    
    <content type="html"><![CDATA[<p>查找资料的方法：</p><ol><li>微信公众号里面搜索</li><li>CSDN里面搜索？</li><li>简书里面搜索？</li><li></li></ol><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="值得关注的网址："><a href="#值得关注的网址：" class="headerlink" title="值得关注的网址："></a>值得关注的网址：</h1><p><a href="http://wiki.jikexueyuan.com/project/deep-android-v2/activity.html" target="_blank" rel="noopener">极客学院-深入理解 Android 卷II</a><br>有SystemServer/PackageManagerService/PowerManagerService/ActivityManagerService/ContentProvider等讲解</p><h1 id="编程刷题网站："><a href="#编程刷题网站：" class="headerlink" title="编程刷题网站："></a>编程刷题网站：</h1><p><a href="https://leetcode-cn.com/" target="_blank" rel="noopener">https://leetcode-cn.com/</a><br><a href="https://www.jiuzhang.com/solutions/" target="_blank" rel="noopener">https://www.jiuzhang.com/solutions/</a><br><a href="http://www.codevs.cn/" target="_blank" rel="noopener">http://www.codevs.cn/</a><br><a href="https://www.hackerrank.com/onboarding/challenge" target="_blank" rel="noopener">https://www.hackerrank.com/onboarding/challenge</a><br><a href="http://www.codeding.com/" target="_blank" rel="noopener">http://www.codeding.com/</a></p><h1 id="语言手册"><a href="#语言手册" class="headerlink" title="语言手册"></a>语言手册</h1><p><a href="https://mirrors.segmentfault.com/" target="_blank" rel="noopener">https://mirrors.segmentfault.com/</a><br>Introduction to Tornado 中文翻译<br>Android 设计指南简体中文版<br>Swift语言指南<br>PHP 中文手册<br>Node.js 手册<br>Golang 手册<br>Python 2.x 手册<br>Python 3.x 手册<br>Laravel 手册<br>Rust 手册<br>React 手册<br>Kotlin 手册</p><h1 id="RSS-源"><a href="#RSS-源" class="headerlink" title="RSS 源"></a>RSS 源</h1><p>RSS源搜索引擎<br><a href="http://ctrlq.org/rss/" target="_blank" rel="noopener">Instant RSS Search</a>是一个谷歌驱动的RSS搜索引擎,中文搜索不大好用</p><p><a href="https://www.zhihu.com/question/19580096" target="_blank" rel="noopener">你必读的 RSS 订阅源有哪些？</a></p><p>博客<br>MyBlog： <a href="https://ericchows.github.io/atom.xml" target="_blank" rel="noopener">https://ericchows.github.io/atom.xml</a><br>罗升阳的博客： <a href="https://blog.csdn.net/luoshengyang/rss/list" target="_blank" rel="noopener">https://blog.csdn.net/luoshengyang/rss/list</a></p><p>技术小黑屋： <a href="http://droidyue.com/atom.xml" target="_blank" rel="noopener">http://droidyue.com/atom.xml</a><br>月光博客： <a href="http://feed.williamlong.info/" target="_blank" rel="noopener">http://feed.williamlong.info/</a><br>极客公园： <a href="http://www.geekpark.net/rss" target="_blank" rel="noopener">http://www.geekpark.net/rss</a></p><p>软件推荐<br>小众软件： <a href="https://feeds.appinn.com/appinns/" target="_blank" rel="noopener">https://feeds.appinn.com/appinns/</a><br>少数派： <a href="https://sspai.com/feed" target="_blank" rel="noopener">https://sspai.com/feed</a><br>反斗软件: <a href="http://www.apprcn.com/feed" target="_blank" rel="noopener">http://www.apprcn.com/feed</a><br>善用佳软： <a href="https://xbeta.info/feed" target="_blank" rel="noopener">https://xbeta.info/feed</a></p><p>知乎日报： <a href="http://feeds.feedburner.com/zhihu-daily" target="_blank" rel="noopener">http://feeds.feedburner.com/zhihu-daily</a><br>知乎每日精选： zhihu.com/rss<br>果壳网（科普生活）：<a href="http://www.guokr.com/rss/" target="_blank" rel="noopener">http://www.guokr.com/rss/</a><br>科学松鼠会（科普）：<a href="http://songshuhui.net/feed" target="_blank" rel="noopener">http://songshuhui.net/feed</a></p><p>战隼的学习探索（效率生活）：<a href="http://feed.read.org.cn/" target="_blank" rel="noopener">http://feed.read.org.cn/</a></p><h1 id="兼职"><a href="#兼职" class="headerlink" title="兼职"></a>兼职</h1><p><a href="https://www.yuanjisong.com/" target="_blank" rel="noopener">猿急送</a> 供兼职程序员信息，是一个高级技术共享平台,可以干兼职<br><a href="https://www.proginn.com/" target="_blank" rel="noopener">程序员客栈</a><br><a href="https://codemart.com/" target="_blank" rel="noopener">码市</a></p><h1 id="有意思的网站"><a href="#有意思的网站" class="headerlink" title="有意思的网站"></a>有意思的网站</h1><p><a href="https://news.ycombinator.com/newest" target="_blank" rel="noopener">Hacker News</a><br>HackerNews也是深受广大技术人员欢迎的技术分享交流网站。虽然HackerNews被很多人吐槽网站界面很low，但毕竟访客的需求就是如此的明确，简单。所以有的时候浏览网站内容，能提供一个RSS订阅地址足矣。</p><p><a href="https://www.hongkiat.com/blog/" target="_blank" rel="noopener">hongkiat</a><br>hongkiat是与技术、设计领域相关的站点之一，大家可以在这里分享技术文章。</p><h1 id="小程序"><a href="#小程序" class="headerlink" title="小程序"></a>小程序</h1><p>源码<br><a href="https://www.cnblogs.com/shenzikun1314/p/7805168.html" target="_blank" rel="noopener">https://www.cnblogs.com/shenzikun1314/p/7805168.html</a><br><a href="https://www.cnblogs.com/tuyile006/p/6268961.html" target="_blank" rel="noopener">https://www.cnblogs.com/tuyile006/p/6268961.html</a><br><a href="https://download.csdn.net/download/beetler2004/10136459" target="_blank" rel="noopener">https://download.csdn.net/download/beetler2004/10136459</a></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://www.360doc.com/content/16/0701/22/202378_572290345.shtml" target="_blank" rel="noopener">IT学习方法论</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;查找资料的方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;微信公众号里面搜索&lt;/li&gt;
&lt;li&gt;CSDN里面搜索？&lt;/li&gt;
&lt;li&gt;简书里面搜索？&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自
      
    
    </summary>
    
      <category term="方法论" scheme="http://yoursite.com/categories/%E6%96%B9%E6%B3%95%E8%AE%BA/"/>
    
      <category term="IT" scheme="http://yoursite.com/categories/%E6%96%B9%E6%B3%95%E8%AE%BA/IT/"/>
    
    
      <category term="IT" scheme="http://yoursite.com/tags/IT/"/>
    
      <category term="方法论" scheme="http://yoursite.com/tags/%E6%96%B9%E6%B3%95%E8%AE%BA/"/>
    
  </entry>
  
  <entry>
    <title>Android 开发者指南阅读</title>
    <link href="http://yoursite.com/Android%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97%E9%98%85%E8%AF%BB/"/>
    <id>http://yoursite.com/Android开发者指南阅读/</id>
    <published>2017-05-02T13:23:33.000Z</published>
    <updated>2018-08-10T03:36:16.249Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://developer.android.com/guide/" target="_blank" rel="noopener">Android 开发者指南</a><br>进入<a href="https://developer.android.com" target="_blank" rel="noopener">https://developer.android.com</a> -&gt; Doc -&gt; Guides即可</p><p>对应的中文版请点<a href="https://mirrors.segmentfault.com/" target="_blank" rel="noopener">这里</a></p><p>由于东西太多，就先从自己感兴趣的开始翻译。<br>已经完成：<br>Best practices<br>└── Performance</p><h1 id="APP-Basics"><a href="#APP-Basics" class="headerlink" title="APP Basics"></a>APP Basics</h1><p>应用基础知识</p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><h2 id="Build-your-first-app"><a href="#Build-your-first-app" class="headerlink" title="Build your first app"></a>Build your first app</h2><h2 id="App-fundamentals"><a href="#App-fundamentals" class="headerlink" title="App fundamentals"></a>App fundamentals</h2><h2 id="App-resources"><a href="#App-resources" class="headerlink" title="App resources"></a>App resources</h2><h2 id="App-manifest-file"><a href="#App-manifest-file" class="headerlink" title="App manifest file"></a>App manifest file</h2><h2 id="App-permissions"><a href="#App-permissions" class="headerlink" title="App permissions"></a>App permissions</h2><h1 id="Devices"><a href="#Devices" class="headerlink" title="Devices"></a>Devices</h1><p>设备</p><h2 id="Device-compatibility"><a href="#Device-compatibility" class="headerlink" title="Device compatibility"></a>Device compatibility</h2><h2 id="Wear"><a href="#Wear" class="headerlink" title="Wear"></a>Wear</h2><h2 id="Android-TV"><a href="#Android-TV" class="headerlink" title="Android TV"></a>Android TV</h2><h2 id="Android-Auto"><a href="#Android-Auto" class="headerlink" title="Android Auto"></a>Android Auto</h2><h2 id="Android-Things"><a href="#Android-Things" class="headerlink" title="Android Things"></a>Android Things</h2><h2 id="Chrome-OS-devices"><a href="#Chrome-OS-devices" class="headerlink" title="Chrome OS devices"></a>Chrome OS devices</h2><h1 id="Core-topics"><a href="#Core-topics" class="headerlink" title="Core topics"></a>Core topics</h1><h2 id="Activities"><a href="#Activities" class="headerlink" title="Activities"></a>Activities</h2><h3 id="Introduction-to-activities"><a href="#Introduction-to-activities" class="headerlink" title="Introduction to activities"></a>Introduction to activities</h3><h3 id="The-activity-lifecycle"><a href="#The-activity-lifecycle" class="headerlink" title="The activity lifecycle"></a>The activity lifecycle</h3><h3 id="Activity-state-changes"><a href="#Activity-state-changes" class="headerlink" title="Activity state changes"></a>Activity state changes</h3><h3 id="Tasks-and-back-stack"><a href="#Tasks-and-back-stack" class="headerlink" title="Tasks and back stack"></a>Tasks and back stack</h3><h3 id="Processes-and-app-lifecycle"><a href="#Processes-and-app-lifecycle" class="headerlink" title="Processes and app lifecycle"></a>Processes and app lifecycle</h3><h3 id="Parcelables-and-bundles"><a href="#Parcelables-and-bundles" class="headerlink" title="Parcelables and bundles"></a>Parcelables and bundles</h3><h3 id="Fragments"><a href="#Fragments" class="headerlink" title="Fragments"></a>Fragments</h3><h3 id="Interact-with-other-apps"><a href="#Interact-with-other-apps" class="headerlink" title="Interact with other apps"></a>Interact with other apps</h3><h3 id="Handling-app-links"><a href="#Handling-app-links" class="headerlink" title="Handling app links"></a>Handling app links</h3><h3 id="Loaders"><a href="#Loaders" class="headerlink" title="Loaders"></a>Loaders</h3><h3 id="Recents-screen"><a href="#Recents-screen" class="headerlink" title="Recents screen"></a>Recents screen</h3><h3 id="Multi-window-support"><a href="#Multi-window-support" class="headerlink" title="Multi-window support"></a>Multi-window support</h3><h3 id="App-shorcuts"><a href="#App-shorcuts" class="headerlink" title="App shorcuts"></a>App shorcuts</h3><h3 id="App-widgets"><a href="#App-widgets" class="headerlink" title="App widgets"></a>App widgets</h3><h2 id="Intents-and-intent-filters"><a href="#Intents-and-intent-filters" class="headerlink" title="Intents and intent filters"></a>Intents and intent filters</h2><h3 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Common-intents"><a href="#Common-intents" class="headerlink" title="Common intents"></a>Common intents</h3><h2 id="User-interface-amp-navigation"><a href="#User-interface-amp-navigation" class="headerlink" title="User interface &amp; navigation"></a>User interface &amp; navigation</h2><h3 id="Overview-1"><a href="#Overview-1" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Layouts"><a href="#Layouts" class="headerlink" title="Layouts"></a>Layouts</h3><h3 id="Look-and-feel"><a href="#Look-and-feel" class="headerlink" title="Look and feel"></a>Look and feel</h3><h3 id="Notifications"><a href="#Notifications" class="headerlink" title="Notifications"></a>Notifications</h3><h3 id="Add-the-app-bar"><a href="#Add-the-app-bar" class="headerlink" title="Add the app bar"></a>Add the app bar</h3><h3 id="Control-the-system-UI-visibility"><a href="#Control-the-system-UI-visibility" class="headerlink" title="Control the system UI visibility"></a>Control the system UI visibility</h3><h3 id="Designing-effective-navigation"><a href="#Designing-effective-navigation" class="headerlink" title="Designing effective navigation"></a>Designing effective navigation</h3><h3 id="Implementing-effective"><a href="#Implementing-effective" class="headerlink" title="Implementing effective"></a>Implementing effective</h3><h3 id="navigation"><a href="#navigation" class="headerlink" title="navigation"></a>navigation</h3><h3 id="Slide-between-fragments-using-ViewPager"><a href="#Slide-between-fragments-using-ViewPager" class="headerlink" title="Slide between fragments using ViewPager"></a>Slide between fragments using ViewPager</h3><h3 id="Supporting-swipe-to-refresh"><a href="#Supporting-swipe-to-refresh" class="headerlink" title="Supporting swipe-to-refresh"></a>Supporting swipe-to-refresh</h3><h3 id="Toasts-overview"><a href="#Toasts-overview" class="headerlink" title="Toasts overview"></a>Toasts overview</h3><h3 id="Pop-up-messages-overview"><a href="#Pop-up-messages-overview" class="headerlink" title="Pop-up messages overview"></a>Pop-up messages overview</h3><h3 id="Dialogs"><a href="#Dialogs" class="headerlink" title="Dialogs"></a>Dialogs</h3><h3 id="Menus"><a href="#Menus" class="headerlink" title="Menus"></a>Menus</h3><h3 id="Settings"><a href="#Settings" class="headerlink" title="Settings"></a>Settings</h3><h3 id="Search"><a href="#Search" class="headerlink" title="Search"></a>Search</h3><h3 id="Copy-and-paste"><a href="#Copy-and-paste" class="headerlink" title="Copy and paste"></a>Copy and paste</h3><h3 id="Drag-and-drop"><a href="#Drag-and-drop" class="headerlink" title="Drag and drop"></a>Drag and drop</h3><h3 id="Creating-backward-compatible"><a href="#Creating-backward-compatible" class="headerlink" title="Creating backward-compatible"></a>Creating backward-compatible</h3><h3 id="Uis"><a href="#Uis" class="headerlink" title="Uis"></a>Uis</h3><h2 id="Animations-amp-transitions"><a href="#Animations-amp-transitions" class="headerlink" title="Animations &amp; transitions"></a>Animations &amp; transitions</h2><h3 id="Overview-2"><a href="#Overview-2" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Animations-overview"><a href="#Animations-overview" class="headerlink" title="Animations overview"></a>Animations overview</h3><h3 id="Property-animation-overview"><a href="#Property-animation-overview" class="headerlink" title="Property animation overview"></a>Property animation overview</h3><h3 id="Animate-drawable-graphics"><a href="#Animate-drawable-graphics" class="headerlink" title="Animate drawable graphics"></a>Animate drawable graphics</h3><h3 id="Reveal-or-hide-a-view-using-animation"><a href="#Reveal-or-hide-a-view-using-animation" class="headerlink" title="Reveal or hide a view using animation"></a>Reveal or hide a view using animation</h3><h3 id="Move-a-view-using-animation"><a href="#Move-a-view-using-animation" class="headerlink" title="Move a view using animation"></a>Move a view using animation</h3><h3 id="Move-a-view-using-fling-animation"><a href="#Move-a-view-using-fling-animation" class="headerlink" title="Move a view using fling animation"></a>Move a view using fling animation</h3><h3 id="Enlarge-a-view-using-a-zoom-animation"><a href="#Enlarge-a-view-using-a-zoom-animation" class="headerlink" title="Enlarge a view using a zoom animation"></a>Enlarge a view using a zoom animation</h3><h3 id="Animate-movement-using-spring-physics"><a href="#Animate-movement-using-spring-physics" class="headerlink" title="Animate movement using spring physics"></a>Animate movement using spring physics</h3><h3 id="Auto-animate-layout-updates"><a href="#Auto-animate-layout-updates" class="headerlink" title="Auto animate layout updates"></a>Auto animate layout updates</h3><h3 id="Animate-layout-changes-using-a-transtiton"><a href="#Animate-layout-changes-using-a-transtiton" class="headerlink" title="Animate layout changes using a transtiton"></a>Animate layout changes using a transtiton</h3><h3 id="Create-a-custom-transition"><a href="#Create-a-custom-transition" class="headerlink" title="Create a custom transition"></a>Create a custom transition</h3><h3 id="Start-an-activity-using-an-animation"><a href="#Start-an-activity-using-an-animation" class="headerlink" title="Start an activity using an animation"></a>Start an activity using an animation</h3><h2 id="Images-amp-graphics"><a href="#Images-amp-graphics" class="headerlink" title="Images &amp; graphics"></a>Images &amp; graphics</h2><h3 id="Overview-3"><a href="#Overview-3" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Drawable-overview"><a href="#Drawable-overview" class="headerlink" title="Drawable overview"></a>Drawable overview</h3><h3 id="Vector-drawables-overview"><a href="#Vector-drawables-overview" class="headerlink" title="Vector drawables overview"></a>Vector drawables overview</h3><h3 id="Handling-bitmaps"><a href="#Handling-bitmaps" class="headerlink" title="Handling bitmaps"></a>Handling bitmaps</h3><h3 id="Selecting-colors-with-the-palette-API"><a href="#Selecting-colors-with-the-palette-API" class="headerlink" title="Selecting colors with the palette API"></a>Selecting colors with the palette API</h3><h3 id="Reducing-image-download-size"><a href="#Reducing-image-download-size" class="headerlink" title="Reducing image download size"></a>Reducing image download size</h3><h3 id="Hardware-accelertation"><a href="#Hardware-accelertation" class="headerlink" title="Hardware accelertation"></a>Hardware accelertation</h3><h3 id="OpenGL-ES"><a href="#OpenGL-ES" class="headerlink" title="OpenGL ES"></a>OpenGL ES</h3><h3 id="Displaying-graphics-with-OpenGL"><a href="#Displaying-graphics-with-OpenGL" class="headerlink" title="Displaying graphics with OpenGL"></a>Displaying graphics with OpenGL</h3><h3 id="ES"><a href="#ES" class="headerlink" title="ES"></a>ES</h3><h3 id="Rendering"><a href="#Rendering" class="headerlink" title="Rendering"></a>Rendering</h3><h3 id="Enhancing-graphics-with-wide-color-content"><a href="#Enhancing-graphics-with-wide-color-content" class="headerlink" title="Enhancing graphics with wide color content"></a>Enhancing graphics with wide color content</h3><h2 id="Audio-amp-Video"><a href="#Audio-amp-Video" class="headerlink" title="Audio &amp; Video"></a>Audio &amp; Video</h2><h3 id="Audio-amp-video-overview"><a href="#Audio-amp-video-overview" class="headerlink" title="Audio &amp; video overview"></a>Audio &amp; video overview</h3><h3 id="Supported-media-formats"><a href="#Supported-media-formats" class="headerlink" title="Supported media formats"></a>Supported media formats</h3><h3 id="Media-app-architecture"><a href="#Media-app-architecture" class="headerlink" title="Media app architecture"></a>Media app architecture</h3><h3 id="Routing-between-devices"><a href="#Routing-between-devices" class="headerlink" title="Routing between devices"></a>Routing between devices</h3><h3 id="Control-amplitude-with-VolumeShaper"><a href="#Control-amplitude-with-VolumeShaper" class="headerlink" title="Control amplitude with VolumeShaper"></a>Control amplitude with VolumeShaper</h3><h3 id="MediaPlayer-overview"><a href="#MediaPlayer-overview" class="headerlink" title="MediaPlayer overview"></a>MediaPlayer overview</h3><h3 id="MediaRecorder-overview"><a href="#MediaRecorder-overview" class="headerlink" title="MediaRecorder overview"></a>MediaRecorder overview</h3><h3 id="ExoPlayer"><a href="#ExoPlayer" class="headerlink" title="ExoPlayer"></a>ExoPlayer</h3><h2 id="Background-tasks"><a href="#Background-tasks" class="headerlink" title="Background tasks"></a>Background tasks</h2><h3 id="Overview-4"><a href="#Overview-4" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Guide-to-background-processing"><a href="#Guide-to-background-processing" class="headerlink" title="Guide to background processing"></a>Guide to background processing</h3><h3 id="Sending-operations-to-multiple"><a href="#Sending-operations-to-multiple" class="headerlink" title="Sending operations to multiple"></a>Sending operations to multiple</h3><h3 id="threads"><a href="#threads" class="headerlink" title="threads"></a>threads</h3><h3 id="Schedule-jobs-intelligently"><a href="#Schedule-jobs-intelligently" class="headerlink" title="Schedule jobs intelligently"></a>Schedule jobs intelligently</h3><h3 id="Services"><a href="#Services" class="headerlink" title="Services"></a>Services</h3><h3 id="Background-optimizations"><a href="#Background-optimizations" class="headerlink" title="Background optimizations"></a>Background optimizations</h3><h3 id="Broadcastes-overview"><a href="#Broadcastes-overview" class="headerlink" title="Broadcastes overview"></a>Broadcastes overview</h3><h3 id="Manage-device-awake-state"><a href="#Manage-device-awake-state" class="headerlink" title="Manage device awake state"></a>Manage device awake state</h3><h2 id="App-data-amp-files"><a href="#App-data-amp-files" class="headerlink" title="App data &amp; files"></a>App data &amp; files</h2><h3 id="Overview-5"><a href="#Overview-5" class="headerlink" title="Overview"></a>Overview</h3><h3 id="Storage-overview"><a href="#Storage-overview" class="headerlink" title="Storage overview"></a>Storage overview</h3><h3 id="Save-files-on-device-storage"><a href="#Save-files-on-device-storage" class="headerlink" title="Save files on device storage"></a>Save files on device storage</h3><p>…….太多了，先不加了</p><h1 id="Best-practices"><a href="#Best-practices" class="headerlink" title="Best practices"></a>Best practices</h1><h2 id="Testing"><a href="#Testing" class="headerlink" title="Testing"></a>Testing</h2><h2 id="Performance"><a href="#Performance" class="headerlink" title="Performance"></a>Performance</h2><p>性能</p><h3 id="Overview-6"><a href="#Overview-6" class="headerlink" title="Overview"></a>Overview</h3><p>性能表现在：</p><ul><li>快速启动</li><li>快速响应用户交互<h3 id="Android-Vitals"><a href="#Android-Vitals" class="headerlink" title="Android Vitals"></a>Android Vitals</h3></li></ul><h3 id="Processes-and-Threads-Overview"><a href="#Processes-and-Threads-Overview" class="headerlink" title="Processes and Threads Overview"></a>Processes and Threads Overview</h3><h3 id="Better-Performance-through-Threading"><a href="#Better-Performance-through-Threading" class="headerlink" title="Better Performance through Threading"></a>Better Performance through Threading</h3><h3 id="Optimizing-for-Battery-Life"><a href="#Optimizing-for-Battery-Life" class="headerlink" title="Optimizing for Battery Life"></a>Optimizing for Battery Life</h3><h3 id="Reducing-APK-Size"><a href="#Reducing-APK-Size" class="headerlink" title="Reducing APK Size"></a>Reducing APK Size</h3><h3 id="Manager-Your-App’s-Memory"><a href="#Manager-Your-App’s-Memory" class="headerlink" title="Manager Your App’s Memory"></a>Manager Your App’s Memory</h3><h3 id="Overview-of-Memory-Managemement"><a href="#Overview-of-Memory-Managemement" class="headerlink" title="Overview of Memory Managemement"></a>Overview of Memory Managemement</h3><h3 id="Designing-for-Seamlessness"><a href="#Designing-for-Seamlessness" class="headerlink" title="Designing for Seamlessness"></a>Designing for Seamlessness</h3><h3 id="Keeping-Your-App-Responsive"><a href="#Keeping-Your-App-Responsive" class="headerlink" title="Keeping Your App Responsive"></a>Keeping Your App Responsive</h3><h3 id="Performance-Tips"><a href="#Performance-Tips" class="headerlink" title="Performance Tips"></a>Performance Tips</h3><h3 id="SMP-Primer-for-Android"><a href="#SMP-Primer-for-Android" class="headerlink" title="SMP Primer for Android"></a>SMP Primer for Android</h3><h3 id="Verifying-App-Behavior-on-th-ANdroid-Runtime-ART"><a href="#Verifying-App-Behavior-on-th-ANdroid-Runtime-ART" class="headerlink" title="Verifying App Behavior on th ANdroid Runtime(ART)"></a>Verifying App Behavior on th ANdroid Runtime(ART)</h3><h2 id="Accessibility"><a href="#Accessibility" class="headerlink" title="Accessibility"></a>Accessibility</h2><h2 id="Security"><a href="#Security" class="headerlink" title="Security"></a>Security</h2><h2 id="Build-for-Billions"><a href="#Build-for-Billions" class="headerlink" title="Build for Billions"></a>Build for Billions</h2><h2 id="Build-for-Enterprise"><a href="#Build-for-Enterprise" class="headerlink" title="Build for Enterprise"></a>Build for Enterprise</h2><h2 id="Google-Play"><a href="#Google-Play" class="headerlink" title="Google Play"></a>Google Play</h2>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;https://developer.android.com/guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Android 开发者指南&lt;/a&gt;&lt;br&gt;进入&lt;a href=&quot;https://developer.android.c
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="指南" scheme="http://yoursite.com/tags/%E6%8C%87%E5%8D%97/"/>
    
      <category term="Guides" scheme="http://yoursite.com/tags/Guides/"/>
    
  </entry>
  
  <entry>
    <title>Android中MVC模式和MVP模式</title>
    <link href="http://yoursite.com/Android-MVC-MVP/"/>
    <id>http://yoursite.com/Android-MVC-MVP/</id>
    <published>2015-12-31T16:00:02.000Z</published>
    <updated>2018-07-30T08:43:36.774Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://juejin.im/post/5b3a20da6fb9a024d154a3be?utm_source=gold_browser_extension" target="_blank" rel="noopener">Android：聊聊我所理解的MVP</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="MVC" scheme="http://yoursite.com/categories/Android/MVC/"/>
    
      <category term="MVP" scheme="http://yoursite.com/categories/Android/MVC/MVP/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="MVC" scheme="http://yoursite.com/tags/MVC/"/>
    
      <category term="MVP" scheme="http://yoursite.com/tags/MVP/"/>
    
  </entry>
  
  <entry>
    <title>Chrome</title>
    <link href="http://yoursite.com/Chrome/"/>
    <id>http://yoursite.com/Chrome/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-30T06:40:06.611Z</updated>
    
    <content type="html"><![CDATA[<p>Chrome相关</p><ol><li>Chrome 插件开发<br><a href="https://developer.chrome.com/extensions" target="_blank" rel="noopener">官方文档</a><br><a href="http://open.chrome.360.cn/extension_dev/overview.html" target="_blank" rel="noopener">综述–扩展开发文档</a></li></ol><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://juejin.im/post/5b24956951882574d73c6f78?utm_source=gold_browser_extension" target="_blank" rel="noopener">写个Markdown插件,讲讲Chrome扩展开发</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Chrome相关&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Chrome 插件开发&lt;br&gt;&lt;a href=&quot;https://developer.chrome.com/extensions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官方文档&lt;/a&gt;&lt;br&gt;&lt;a href
      
    
    </summary>
    
      <category term="Chrome" scheme="http://yoursite.com/categories/Chrome/"/>
    
    
      <category term="Chrome" scheme="http://yoursite.com/tags/Chrome/"/>
    
  </entry>
  
  <entry>
    <title>Android中Binder机制解析</title>
    <link href="http://yoursite.com/Android-Binder-Analysis/"/>
    <id>http://yoursite.com/Android-Binder-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-16T13:50:16.228Z</updated>
    
    <content type="html"><![CDATA[<h1 id="什么是-Binder？"><a href="#什么是-Binder？" class="headerlink" title="什么是 Binder？"></a>什么是 Binder？</h1><p>Binder是Android系统中进程间通讯（IPC Inter-Process Communication，进程间通信）的一种方式，也是Android系统中最重要的特性之一。Android中的四大组件Activity，Service，Broadcast，ContentProvider，不同的App等都运行在不同的进程中，它是这些进程间通讯的桥梁。正如其名“粘合剂”一样，它把系统中各个组件粘合到了一起，是各个组件的桥梁。</p><p>Binder在Android中的实际应用有哪些？</p><p>Binder的个数有没有限制？</p><h1 id="Binder-架构"><a href="#Binder-架构" class="headerlink" title="Binder 架构"></a>Binder 架构</h1><p><img src="/Android-Binder-Analysis/Binder-01.jpg" alt="&quot;Binder 架构&quot;"></p><p>• Binder 通信采用 C/S 架构，从组件视角来说，包含 Client、 Server、 ServiceManager 以及 Binder 驱动，其中 ServiceManager 用于管理系统中的各种服务。<br>• Binder 在 framework 层进行了封装，通过 JNI 技术调用 Native（C/C++）层的 Binder 架构。<br>• Binder 在 Native 层以 ioctl 的方式与 Binder 驱动通讯。</p><h1 id="Binder-机制"><a href="#Binder-机制" class="headerlink" title="Binder 机制"></a>Binder 机制</h1><p><img src="/Android-Binder-Analysis/Binder-02.jpg" alt="&quot;Binder 机制&quot;"></p><p>•首先需要注册服务端，只有注册了服务端，客户端才有通讯的目标，服务端通过 ServiceManager 注册服务，注册的过程就是向 Binder 驱动的全局链表 binder_procs 中插入服务端的信息（binder_proc 结构体，每个 binder_proc 结构体中都有 todo 任务队列），然后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。</p><p>•有了服务端，客户端就可以跟服务端通讯了，通讯之前需要先获取到服务，拿到服务的代理，也可以理解为引用。比如下面的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取WindowManager服务引用</span></span><br><span class="line">WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);</span><br></pre></td></tr></table></figure></p><p>获取服务端的方式就是通过 ServiceManager 向 svcinfo 列表中查询一下返回服务端的代理，svcinfo 列表就是所有已注册服务的通讯录，保存了所有注册的服务信息。</p><p>•有了服务端的引用我们就可以向服务端发送请求了，通过 BinderProxy 将我们的请求参数发送给 ServiceManager，通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间，这时我们的客户端进入等待状态，然后 Binder 驱动向服务端的 todo 队列里面插入一条事务，执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间（这里只是执行了拷贝命令，并没有拷贝数据，binder只进行一次拷贝），唤醒等待的客户端并把结果响应回来，这样就完成了一次通讯。</p><h1 id="Binder-驱动"><a href="#Binder-驱动" class="headerlink" title="Binder 驱动"></a>Binder 驱动</h1><p>我们先来了解下用户空间与内核空间是怎么交互的<br><img src="/Android-Binder-Analysis/Binder-03.jpg" alt="&quot;Binder 驱动&quot;"></p><h2 id="用户空间-内核空间"><a href="#用户空间-内核空间" class="headerlink" title="用户空间/内核空间"></a>用户空间/内核空间</h2><p>详细解释可以参考 <a href="http://www.linfo.org/kernel_space.html" target="_blank" rel="noopener">Kernel Space Definition</a>； 简单理解如下：<br>Kernel space 是 Linux 内核的运行空间，User space 是用户程序的运行空间。 为了安全，它们是隔离的，即使用户的程序崩溃了，内核也不受影响。<br>Kernel space 可以执行任意命令，调用系统的一切资源； User space 只能执行简单的运算，不能直接调用系统资源，必须通过系统接口（又称 system call），才能向内核发出指令。</p><h2 id="系统调用-内核态-用户态"><a href="#系统调用-内核态-用户态" class="headerlink" title="系统调用/内核态/用户态"></a>系统调用/内核态/用户态</h2><p>虽然从逻辑上抽离出用户空间和内核空间；但是不可避免的的是，总有那么一些用户空间需要访问内核的资源；比如应用程序访问文件，网络是很常见的事情，怎么办呢？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Kernel space can be accessed by user processes only through the use of system calls.</span><br></pre></td></tr></table></figure></p><p>用户空间访问内核空间的唯一方式就是系统调用；通过这个统一入口接口，所有的资源访问都是在内核的控制下执行，以免导致对用户程序对系统资源的越权访问，从而保障了系统的安全和稳定。用户软件良莠不齐，要是它们乱搞把系统玩坏了怎么办？因此对于某些特权操作必须交给安全可靠的内核来执行。</p><p>当一个任务（进程）执行系统调用而陷入内核代码中执行时，我们就称进程处于内核运行态（或简称为内核态）此时处理器处于特权级最高的（0级）内核代码中执行。当进程在执行用户自己的代码时，则称其处于用户运行态（用户态）。即此时处理器在特权级最低的（3级）用户代码中运行。处理器在特权等级高的时候才能执行那些特权CPU指令。</p><h2 id="内核模块-驱动"><a href="#内核模块-驱动" class="headerlink" title="内核模块/驱动"></a>内核模块/驱动</h2><p>通过系统调用，用户空间可以访问内核空间，那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢？很自然想到的是让操作系统内核添加支持；传统的 Linux 通信机制，比如 Socket，管道等都是内核支持的；但是 Binder 并不是 Linux 内核的一部分，它是怎么做到访问内核空间的呢？ Linux 的动态可加载内核模块（Loadable Kernel Module，LKM）机制解决了这个问题；模块是具有独立功能的程序，它可以被单独编译，但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。这样，Android系统可以通过添加一个内核模块运行在内核空间，用户进程之间的通过这个模块作为桥梁，就可以完成通信了。</p><p>在 Android 系统中，这个运行在内核空间的，负责各个用户进程通过 Binder 通信的内核模块叫做 Binder 驱动;</p><blockquote><p>驱动程序一般指的是设备驱动程序（Device Driver），是一种可以使计算机和设备通信的特殊程序。相当于硬件的接口，操作系统只有通过这个接口，才能控制硬件设备的工作；</p></blockquote><p>驱动就是操作硬件的接口，为了支持Binder通信过程，Binder 使用了一种“硬件”，因此这个模块被称之为驱动。</p><p>熟悉了上面这些概念，我们再来看下上面的图，用户空间中 binder_open(), binder_mmap(), binder_ioctl() 这些方法通过 system call 来调用内核空间 Binder 驱动中的方法。内核空间与用户空间共享内存通过 copy_from_user(), copy_to_user() 内核方法来完成用户空间与内核空间内存的数据传输。 Binder驱动中有一个全局的 binder_procs 链表保存了服务端的进程信息。</p><h1 id="Binder-进程与线程"><a href="#Binder-进程与线程" class="headerlink" title="Binder 进程与线程"></a>Binder 进程与线程</h1><p><img src="/Android-Binder-Analysis/Binder-04.jpg" alt="&quot;Binder 进程与线程&quot;"><br>对于底层Binder驱动，通过 binder_procs 链表记录所有创建的 binder_proc 结构体，binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应，且每个进程有且只有一个 ProcessState 对象，这是通过单例模式来保证的。在每个进程中可以有很多个线程，每个线程对应一个 IPCThreadState 对象，IPCThreadState 对象也是单例模式，即一个线程对应一个 IPCThreadState 对象，在 Binder 驱动层也有与之相对应的结构，那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads，来记录当前进程内所有的 binder_thread。</p><p>Binder 线程池：每个 Server 进程在启动时创建一个 binder 线程池，并向其中注册一个 Binder 线程；之后 Server 进程也可以向 binder 线程池注册新的线程，或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制，默认为16个 binder 线程，例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的 binder 请求都是交由 Server 端进程的 binder 线程来处理的。</p><h1 id="ServiceManager-启动"><a href="#ServiceManager-启动" class="headerlink" title="ServiceManager 启动"></a>ServiceManager 启动</h1><p>了解了 Binder 驱动，怎么与 Binder 驱动进行通讯呢？那就是通过 ServiceManager，好多文章称 ServiceManager 是 Binder 驱动的守护进程，大管家，其实 ServiceManager 的作用很简单就是提供了查询服务和注册服务的功能。下面我们来看一下 ServiceManager 启动的过程。</p><p><img src="/Android-Binder-Analysis/Binder-05.jpg" alt="&quot;ServiceManager 启动&quot;"></p><p> ServiceManager 分为 framework 层和 native 层，framework 层只是对 native 层进行了封装方便调用，图上展示的是 native 层的 ServiceManager 启动过程。</p><p>• ServiceManager 的启动是系统在开机时，init 进程解析 init.rc 文件调用 service_manager.c 中的 main() 方法入口启动的。 native 层有一个 binder.c 封装了一些与 Binder 驱动交互的方法。</p><p>• ServiceManager 的启动分为三步，首先打开驱动创建全局链表 binder_procs，然后将自己当前进程信息保存到 binder_procs 链表，最后开启 loop 不断的处理共享内存中的数据，并处理 BR_xxx 命令（ioctl 的命令，BR 可以理解为 binder reply 驱动处理完的响应）。</p><h1 id="ServiceManager-注册服务"><a href="#ServiceManager-注册服务" class="headerlink" title="ServiceManager 注册服务"></a>ServiceManager 注册服务</h1><p><img src="/Android-Binder-Analysis/Binder-06.jpg" alt="&quot;ServiceManager 注册服务&quot;"></p><p>• 注册 MediaPlayerService 服务端，我们通过 ServiceManager 的 addService() 方法来注册服务。</p><p>• 首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令（ioctl 的命令，BC 可以理解为 binder client 客户端发过来的请求命令）携带 ADD_SERVICE_TRANSACTION 命令，同时注册服务的线程进入等待状态 waitForResponse()。 Binder 驱动收到请求命令向 ServiceManager 的 todo 队列里面添加一条注册服务的事务。事务的任务就是创建服务端进程 binder_node 信息并插入到 binder_procs 链表中。</p><p>• 事务处理完之后发送 BR_TRANSACTION 命令，ServiceManager 收到命令后向 svcinfo 列表中添加已经注册的服务。最后发送 BR_REPLY 命令唤醒等待的线程，通知注册成功。</p><h1 id="ServiceManager-获取服务"><a href="#ServiceManager-获取服务" class="headerlink" title="ServiceManager 获取服务"></a>ServiceManager 获取服务</h1><p><img src="/Android-Binder-Analysis/Binder-07.jpg" alt="&quot;ServiceManager 获取服务&quot;"></p><p>• 获取服务的过程与注册类似，相反的过程。通过 ServiceManager 的 getService() 方法来注册服务。</p><p>• 首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令携带 CHECK_SERVICE_TRANSACTION 命令，同时获取服务的线程进入等待状态 waitForResponse()。</p><p>• Binder 驱动收到请求命令向 ServiceManager 的发送 BC_TRANSACTION 查询已注册的服务，查询到直接响应 BR_REPLY 唤醒等待的线程。若查询不到将与 binder_procs 链表中的服务进行一次通讯再响应。</p><h1 id="进行一次完整通讯"><a href="#进行一次完整通讯" class="headerlink" title="进行一次完整通讯"></a>进行一次完整通讯</h1><p><img src="/Android-Binder-Analysis/Binder-08.jpg" alt="&quot;Binder 进行一次完整通讯&quot;"></p><p>• 我们在使用 Binder 时基本都是调用 framework 层封装好的方法，AIDL 就是 framework 层提供的傻瓜式是使用方式。假设服务已经注册完，我们来看看客户端怎么执行服务端的方法。</p><p>• 首先我们通过 ServiceManager 获取到服务端的 BinderProxy 代理对象，通过调用 BinderProxy 将参数，方法标识（例如：TRANSACTION_test，AIDL中自动生成）传给 ServiceManager，同时客户端线程进入等待状态。</p><p>• ServiceManager 将用户空间的参数等请求数据复制到内核空间，并向服务端插入一条执行执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间，并唤醒等待的线程，响应结果，通讯结束。</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="http://weishu.me/2016/01/12/binder-index-for-newer/" target="_blank" rel="noopener">Binder学习指南</a><br><a href="https://blog.csdn.net/freekiteyu/article/details/70082302" target="_blank" rel="noopener">Android-Binder进程间通讯机制</a>★★★★★</p><p>老罗系列<br><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6618363" target="_blank" rel="noopener">Android进程间通信（IPC）机制Binder简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6621566" target="_blank" rel="noopener">浅谈Service Manager成为Android进程间通信（IPC）机制Binder守护进程之路</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6627260" target="_blank" rel="noopener">浅谈Android系统进程间通信（IPC）机制Binder中的Server和Client获得Service Manager接口之路</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6629298" target="_blank" rel="noopener">Android系统进程间通信（IPC）机制Binder中的Server启动过程源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6633311" target="_blank" rel="noopener">Android系统进程间通信（IPC）机制Binder中的Client获得Server远程接口过程源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6642463" target="_blank" rel="noopener">Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析</a></p><p><a href="https://blog.csdn.net/qian520ao/article/details/78089877" target="_blank" rel="noopener">Android Binder之应用层总结与分析</a><br><a href="https://cloud.tencent.com/developer/article/1005763" target="_blank" rel="noopener">Binder 总体架构及相关代码浅析</a><br><a href="http://weishu.me/2016/01/12/binder-index-for-newer/" target="_blank" rel="noopener">Binder学习指南</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&amp;mid=2650241680&amp;idx=1&amp;sn=14648a37ff1e7844e33cd548b9ec5a71&amp;chksm=88638bffbf1402e9b6dbe6b186bc619a341fc5fe0fd2aefd4a7dd3ac70d0539573c48863de0f#rd" target="_blank" rel="noopener">带你一起剖析Android AIDL跨进程通信的实现原理</a></p><p>小米系统工程师–Gityuan 系列<br><a href="http://gityuan.com/2015/10/31/binder-prepare/" target="_blank" rel="noopener">Binder系列—开篇</a><br><a href="http://gityuan.com/2015/11/01/binder-driver/" target="_blank" rel="noopener">Binder系列1—Binder Driver初探</a><br><a href="http://gityuan.com/2015/11/02/binder-driver-2/" target="_blank" rel="noopener">Binder系列2—Binder Driver再探</a><br><a href="http://gityuan.com/2015/11/07/binder-start-sm/" target="_blank" rel="noopener">Binder系列3—启动ServiceManager</a><br><a href="http://gityuan.com/2015/11/08/binder-get-sm/" target="_blank" rel="noopener">Binder系列4—获取ServiceManager</a><br><a href="http://gityuan.com/2015/11/14/binder-add-service/" target="_blank" rel="noopener">Binder系列5—注册服务(addService)</a><br><a href="http://gityuan.com/2015/11/15/binder-get-service/" target="_blank" rel="noopener">Binder系列6—获取服务(getService)</a><br><a href="http://gityuan.com/2015/11/21/binder-framework/" target="_blank" rel="noopener">Binder系列7—framework层分析</a><br><a href="http://gityuan.com/2015/11/22/binder-use/" target="_blank" rel="noopener">Binder系列8—如何使用Binder</a><br><a href="http://gityuan.com/2015/11/23/binder-aidl/" target="_blank" rel="noopener">Binder系列9—如何使用AIDL</a><br><a href="http://gityuan.com/2015/11/28/binder-summary/" target="_blank" rel="noopener">Binder系列10—总结</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;什么是-Binder？&quot;&gt;&lt;a href=&quot;#什么是-Binder？&quot; class=&quot;headerlink&quot; title=&quot;什么是 Binder？&quot;&gt;&lt;/a&gt;什么是 Binder？&lt;/h1&gt;&lt;p&gt;Binder是Android系统中进程间通讯（IPC Inter-P
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Binder" scheme="http://yoursite.com/categories/Android/Binder/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Binder" scheme="http://yoursite.com/tags/Binder/"/>
    
  </entry>
  
  <entry>
    <title>Android中Process</title>
    <link href="http://yoursite.com/Android-Process-Analysis/"/>
    <id>http://yoursite.com/Android-Process-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-18T13:32:46.641Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://mp.weixin.qq.com/s/r8Dxc7cmJbYXZ8E9EvbSkw" target="_blank" rel="noopener">解读Android进程优先级ADJ算法</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Process" scheme="http://yoursite.com/categories/Android/Process/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Process" scheme="http://yoursite.com/tags/Process/"/>
    
  </entry>
  
  <entry>
    <title>Android中Sample</title>
    <link href="http://yoursite.com/Android-Sample/"/>
    <id>http://yoursite.com/Android-Sample/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-18T13:01:26.070Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://blog.csdn.net/carson_ho/article/details/73250163" target="_blank" rel="noopener">Android：JNI 与 NDK到底是什么？</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Sample" scheme="http://yoursite.com/categories/Android/Sample/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Sample" scheme="http://yoursite.com/tags/Sample/"/>
    
  </entry>
  
  <entry>
    <title>Android中Service解析</title>
    <link href="http://yoursite.com/Android-Service-Analysis/"/>
    <id>http://yoursite.com/Android-Service-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-07T07:22:01.003Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7846923" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7857163" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务的连接过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7867340" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务之间的共享UI元数据（SharedClient）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7884628" target="_blank" rel="noopener">Android应用程序请求SurfaceFlinger服务创建Surface的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7932268" target="_blank" rel="noopener">Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析</a></p><p><a href="https://blog.csdn.net/luoshengyang/article/details/8010977" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8022957" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务的启动过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8046659" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务对帧缓冲区（Frame Buffer）的管理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8062945" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务的线程模型分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8079456" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="SurfaceFlinger" scheme="http://yoursite.com/categories/Android/SurfaceFlinger/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="SurfaceFlinger" scheme="http://yoursite.com/tags/SurfaceFlinger/"/>
    
  </entry>
  
  <entry>
    <title>Android的稳定性</title>
    <link href="http://yoursite.com/Android-Stability/"/>
    <id>http://yoursite.com/Android-Stability/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-10T01:46:12.605Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>Android的稳定性包含以下几个方面：<br>1.ANR<br>2.Crash<br>3.Tombstone<br>4.Freeze<br>5.黑屏<br>6.冻屏</p><p>#稳定性问题分析以及需要的相关log<br>/data/anr/traces.txt<br>/data/system/dropbox<br>/data/tombstones</p><p><a href="https://www.aliyun.com/jiaocheng/2860.html" target="_blank" rel="noopener">Android系统各种稳定性问题所需要的log</a><br><a href="https://blog.csdn.net/u014134180/article/details/78122529" target="_blank" rel="noopener">Android系统各种稳定性问题所需要的log</a><br>adb shell bugreport &gt; bugreport.txt<br>adb shell dumpstate &gt; dumpstate.log  (this command will produce trace log about all process then u need adb pull /data/anr to collect the trace log)<br>adb shell dumpsys &gt; dumpsys.log </p><p>adb pull /data/tombstones (All log file time must be consistent with issue occurred time, it needs to clear /data/anr &amp;; /data/tombstones after stability issue occu)</p><p>System Freeze/ Touch panel freeze系统卡死/屏幕卡死</p><p>adb shell getevent  此命令可以获取实时触屏以及按键事件log<br>adb shell getevent -rtl /dev/input/event0 按键事件 </p><p>open echo w &gt; /proc/sysrq-trigger when capture dmesg and bugreport log as follows:<br>adb root<br>adb remount<br>adb shell<br>echo w &gt; /proc/sysrq-trigger<br>&amp;; then exit adb shell, then collect bugreport<br>adb shell bugreport &gt; bugreport.txt<br>adb shell kmesg &gt; kmesg.txt 没有kmesg<br><a href="https://blog.csdn.net/njuitjf/article/details/12045527" target="_blank" rel="noopener">/proc/sysrq-trigger文件的强大功能</a><br><a href="https://www.cnblogs.com/justin-y-lin/p/5424555.html" target="_blank" rel="noopener">Linux内核调试方法总结之sysrq</a><br><a href="https://blog.csdn.net/beckdon/article/details/41313713" target="_blank" rel="noopener">/proc/sysrq-trigger详解</a></p><p>adb pull /d/binder/ .  所有的binder信息</p><p>adb shell dumpsys window &gt; dump_window.txt </p><p>adb shell cat proc/meminfo &gt; meminfo.txt </p><p>adb shell procrank &gt; procrank.txt </p><p>adb shell top &gt; top.txt </p><p>##检查adb,TP,显卡驱动,按键是否正常工作<br>•Adb workable or not, ANR or not<br>•CTP workable or not -&gt;<br>touch screen and observe<br>the output of “adb shell getevent”.<br>•Display driver workable or not -&gt;<br>Use the screencast to see<br>if the screen can be displayed<br>•Power key/volume key work or not?<br>Menu/back/home key work or not? </p><p>##触发一次Dump，不同平台方式不一样<br>It’s better to trigger a ram dump<br>Before test:<br>adb root<br>adb shell “echo 0x843 &gt; /d/spmi/spmi-0/address”<br>adb shell “echo 0x80 &gt; /d/spmi/spmi-0/data”<br>Then long press power key more than 10~30s<br>could trigger a dump.<br>If device is rebooted, it needs to set again. </p><h2 id="Black-screen-黑屏"><a href="#Black-screen-黑屏" class="headerlink" title="Black screen 黑屏"></a>Black screen 黑屏</h2><p><a href="https://blog.csdn.net/zhangbijun1230/article/details/80739233" target="_blank" rel="noopener">Android系统（110）—稳定性问题总结</a></p><h1 id="ANR"><a href="#ANR" class="headerlink" title="ANR"></a>ANR</h1><p>思路： 机制 –&gt; 触发原因 –&gt; 如何避免 –&gt; 如何分析和解决问题<br><a href="https://blog.csdn.net/wei_lei/article/details/70311702" target="_blank" rel="noopener">Android稳定性专题之ANR</a></p><h1 id="Crash"><a href="#Crash" class="headerlink" title="Crash"></a>Crash</h1><p><a href="https://blog.csdn.net/wei_lei/article/details/69868280" target="_blank" rel="noopener">Android稳定性专题之CRASH</a><br><a href="https://blog.csdn.net/weilaideta/article/details/51348251" target="_blank" rel="noopener">Android系统稳定性—-Crash</a></p><h1 id="Watchdog"><a href="#Watchdog" class="headerlink" title="Watchdog"></a>Watchdog</h1><p>机制/原理 –&gt; 实现方式</p><p><a href="http://rayleeya.iteye.com/blog/1963408" target="_blank" rel="noopener">系统进程的Watchdog</a><br><a href="https://blog.csdn.net/yeqishi/article/details/50262087" target="_blank" rel="noopener">Android Watchdog框架看门狗解析、死锁应用与改造（上）</a><br><a href="https://blog.csdn.net/yeqishi/article/details/50262699" target="_blank" rel="noopener">Android Watchdog框架看门狗解析、死锁应用与改造（下）</a><br><a href="https://blog.csdn.net/lezhang123/article/details/72537647" target="_blank" rel="noopener">Watchdog机制以及问题分析</a><br><a href="http://duanqz.github.io/2015-10-12-Watchdog-Analysis#section-1" target="_blank" rel="noopener">Watchdog机制以及问题分析</a><br><a href="https://blog.csdn.net/wd229047557/article/details/79451969" target="_blank" rel="noopener">Android Watchdog机制介绍与分析</a></p><h1 id="Tombstone"><a href="#Tombstone" class="headerlink" title="Tombstone"></a>Tombstone</h1><h1 id="Panic"><a href="#Panic" class="headerlink" title="Panic"></a>Panic</h1><h1 id="OOM"><a href="#OOM" class="headerlink" title="OOM"></a>OOM</h1><h1 id="稳定性测试"><a href="#稳定性测试" class="headerlink" title="稳定性测试"></a>稳定性测试</h1><p><a href="https://blog.csdn.net/ljd2038/article/details/54561511" target="_blank" rel="noopener">Android稳定性测试工具Monkey的使用</a><br><a href="https://blog.csdn.net/tobetheender/article/details/54693874" target="_blank" rel="noopener">Android稳定性测试– Monkey二次开发</a></p><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://www.jianshu.com/p/32e6bde42185" target="_blank" rel="noopener">安卓App稳定性之旅</a></p><p><a href="http://rayleeya.iteye.com/blog/1956638" target="_blank" rel="noopener">Android 系统稳定性 - OOM（二）</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;Android的稳定性包含以下几个方面：&lt;br&gt;1.ANR&lt;br&gt;2.Crash&lt;br&gt;3.Tombstone&lt;br&gt;4.Freeze&lt;br
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Stability" scheme="http://yoursite.com/categories/Android/Stability/"/>
    
      <category term="稳定性" scheme="http://yoursite.com/categories/Android/Stability/%E7%A8%B3%E5%AE%9A%E6%80%A7/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Stability" scheme="http://yoursite.com/tags/Stability/"/>
    
      <category term="稳定性" scheme="http://yoursite.com/tags/%E7%A8%B3%E5%AE%9A%E6%80%A7/"/>
    
  </entry>
  
  <entry>
    <title>Android中SurfaceFlinger解析</title>
    <link href="http://yoursite.com/Android-SurfaceFlinger-Analysis/"/>
    <id>http://yoursite.com/Android-SurfaceFlinger-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-16T09:48:26.208Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7846923" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务的关系概述和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7857163" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务的连接过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7867340" target="_blank" rel="noopener">Android应用程序与SurfaceFlinger服务之间的共享UI元数据（SharedClient）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7884628" target="_blank" rel="noopener">Android应用程序请求SurfaceFlinger服务创建Surface的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/7932268" target="_blank" rel="noopener">Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析</a></p><p><a href="https://blog.csdn.net/luoshengyang/article/details/8010977" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8022957" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务的启动过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8046659" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务对帧缓冲区（Frame Buffer）的管理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8062945" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务的线程模型分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8079456" target="_blank" rel="noopener">Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="SurfaceFlinger" scheme="http://yoursite.com/categories/Android/SurfaceFlinger/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="SurfaceFlinger" scheme="http://yoursite.com/tags/SurfaceFlinger/"/>
    
  </entry>
  
  <entry>
    <title>SystemUI之灭屏动画</title>
    <link href="http://yoursite.com/SystemUI%E4%B9%8B%E7%81%AD%E5%B1%8F%E5%8A%A8%E7%94%BB/"/>
    <id>http://yoursite.com/SystemUI之灭屏动画/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-09-07T08:11:04.920Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>功能描述：<br>点击Power键，灭屏时，需要显示灭屏动画.</p><h1 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h1><h1 id="实现代码"><a href="#实现代码" class="headerlink" title="实现代码"></a>实现代码</h1><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;功能描述：&lt;br&gt;点击Power键，灭屏时，需要显示灭屏动画.&lt;/p&gt;
&lt;h1 id=&quot;实现原理&quot;&gt;&lt;a href=&quot;#实现原理&quot; clas
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="SystemUI" scheme="http://yoursite.com/categories/Android/SystemUI/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="SystemUI" scheme="http://yoursite.com/tags/SystemUI/"/>
    
  </entry>
  
  <entry>
    <title>Android中启动过程解析</title>
    <link href="http://yoursite.com/A-Ongoing-Android%E5%BC%82%E5%B8%B8%E8%A7%A3%E6%9E%90/"/>
    <id>http://yoursite.com/A-Ongoing-Android异常解析/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-16T09:49:06.185Z</updated>
    
    <content type="html"><![CDATA[<p>android系统常见异常现象有应用无响应、应用停止运行、冻屏、重启、死机</p><p>应用程序无响应：ANR</p><p>应用停止运行：  </p><p>冻屏</p><p>黑屏</p><p>重启</p><p>死机：</p><p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/zwq1457/article/details/75529264" target="_blank" rel="noopener">Android异常分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;android系统常见异常现象有应用无响应、应用停止运行、冻屏、重启、死机&lt;/p&gt;
&lt;p&gt;应用程序无响应：ANR&lt;/p&gt;
&lt;p&gt;应用停止运行：  &lt;/p&gt;
&lt;p&gt;冻屏&lt;/p&gt;
&lt;p&gt;黑屏&lt;/p&gt;
&lt;p&gt;重启&lt;/p&gt;
&lt;p&gt;死机：&lt;/p&gt;
&lt;p&gt;Ongoing,待完成，Ple
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="启动" scheme="http://yoursite.com/categories/Android/%E5%90%AF%E5%8A%A8/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Zegote" scheme="http://yoursite.com/tags/Zegote/"/>
    
      <category term="Init" scheme="http://yoursite.com/tags/Init/"/>
    
  </entry>
  
  <entry>
    <title>WordPress</title>
    <link href="http://yoursite.com/WordPress/"/>
    <id>http://yoursite.com/WordPress/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-15T01:35:20.394Z</updated>
    
    <content type="html"><![CDATA[<h1 id="官网"><a href="#官网" class="headerlink" title="官网"></a>官网</h1><p><a href="https://cn.wordpress.org/download/" target="_blank" rel="noopener">下载</a><br><a href="https://codex.wordpress.org/zh-cn:Main_Page" target="_blank" rel="noopener">官网中文文档</a></p><h1 id="开发步骤"><a href="#开发步骤" class="headerlink" title="开发步骤"></a>开发步骤</h1><ol><li><p>下载WordPress</p></li><li><p>本地调试代码<br>第一步：安装 XAMPP<br>第二步:启动Apache，改端口到8080<br>第三步：启动MySQL，注意：点击Admin的时候，<a href="http://localhost/phpmyadmin/是无法访问的，需要改端口号http://localhost:8080/phpmyadmin/才能访问" target="_blank" rel="noopener">http://localhost/phpmyadmin/是无法访问的，需要改端口号http://localhost:8080/phpmyadmin/才能访问</a><br>新建用户账户: </p></li></ol><p><a href="https://jingyan.baidu.com/article/1e5468f955d65b484961b7bb.html" target="_blank" rel="noopener">本地安装wordpress教程</a></p><ol start="3"><li>安装主题</li></ol><p><a href="https://www.wpdaxue.com/install-wordpress-theme-and-plugin.html" target="_blank" rel="noopener">WordPress入门 之 安装主题和插件</a></p><p>ecommerce-lite 是一个购物商城的模式，暂时不适用</p><p><a href="https://www.diyzhan.com/2014/02/wordpress-admin-panel/" target="_blank" rel="noopener">WordPress的后台功能菜单介绍与操作，WordPress后台说明</a><br><a href="https://www.diyzhan.com/2014/03/wordpress-page-post-product/" target="_blank" rel="noopener">WordPress添加页面，写文章，增加文章类别，增加产品和产品类别</a></p><ol start="4"><li><p>Logo的设计<br>a) <a href="https://www.logosc.cn/" target="_blank" rel="noopener">https://www.logosc.cn/</a> 不错<br>b) <a href="http://www.logomaker.com.cn/logo-maker.jsp" target="_blank" rel="noopener">http://www.logomaker.com.cn/logo-maker.jsp</a></p></li><li><p>wordpress插件或者说是小工具</p></li></ol><h1 id="遇到的问题："><a href="#遇到的问题：" class="headerlink" title="遇到的问题："></a>遇到的问题：</h1><ol><li><p>上传的文件尺寸超过php.ini中定义的upload_max_filesize值。<br>解决方案： <a href="https://www.cnblogs.com/bhlsheji/p/5356564.html" target="_blank" rel="noopener">https://www.cnblogs.com/bhlsheji/p/5356564.html</a></p></li><li><p>二级菜单不生效</p></li></ol><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;官网&quot;&gt;&lt;a href=&quot;#官网&quot; class=&quot;headerlink&quot; title=&quot;官网&quot;&gt;&lt;/a&gt;官网&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://cn.wordpress.org/download/&quot; target=&quot;_blank&quot; rel=&quot;noop
      
    
    </summary>
    
      <category term="WordPress" scheme="http://yoursite.com/categories/WordPress/"/>
    
    
      <category term="WordPress" scheme="http://yoursite.com/tags/WordPress/"/>
    
  </entry>
  
  <entry>
    <title>Android中JNI与NDK</title>
    <link href="http://yoursite.com/Android-JNI-NDK/"/>
    <id>http://yoursite.com/Android-JNI-NDK/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-08T03:13:46.850Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>JNI：Java Native Interface Java本地接口，属于Java的一个特性.<br>1、什么是JNI：<br>    JNI(Java Native Interface):java本地开发接口<br>    JNI是一个协议，这个协议用来沟通java代码和外部的本地代码(c/c++)<br>    外部的c/c++代码也可以调用java代码<br>2、为什么使用JNI：<br>    效率上 C/C++是本地语言，比java更高效<br>    代码移植，如果之前用C语言开发过模块，可以复用已经存在的c代码<br>    java反编译比C语言容易，一般加密算法都是用C语言编写，不容易被反编译</p><p>参考：<a href="https://www.cnblogs.com/rocomp/p/4892866.html" target="_blank" rel="noopener">一天掌握Android JNI本地编程 快速入门</a></p><p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/carson_ho/article/details/73250163" target="_blank" rel="noopener">Android：JNI 与 NDK到底是什么？</a><br><a href="https://blog.csdn.net/column/details/android-jni.html" target="_blank" rel="noopener">Android中JNI入门：专题</a><br><a href="https://www.cnblogs.com/rocomp/p/4892866.html" target="_blank" rel="noopener">一天掌握Android JNI本地编程 快速入门</a> ★★★★★</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;JNI：Java Native Interface Java本地接口，属于Java的一个特性.&lt;br&gt;1、什么是JNI：&lt;br&gt;    JN
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="JNI" scheme="http://yoursite.com/categories/Android/JNI/"/>
    
      <category term="NDK" scheme="http://yoursite.com/categories/Android/JNI/NDK/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="JNI" scheme="http://yoursite.com/tags/JNI/"/>
    
      <category term="NDK" scheme="http://yoursite.com/tags/NDK/"/>
    
  </entry>
  
  <entry>
    <title>SELinux与SEAndroid解析</title>
    <link href="http://yoursite.com/SELinux-SEAndroid/"/>
    <id>http://yoursite.com/SELinux-SEAndroid/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-07T07:31:55.456Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://blog.csdn.net/luoshengyang/article/details/37613135" target="_blank" rel="noopener">SEAndroid安全机制框架分析</a></p><p><a href="https://my.csdn.net/huangyabin001" target="_blank" rel="noopener">SELinux学习</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="SEAndroid" scheme="http://yoursite.com/categories/SEAndroid/"/>
    
      <category term="SELinux" scheme="http://yoursite.com/categories/SEAndroid/SELinux/"/>
    
    
      <category term="SEAndroid" scheme="http://yoursite.com/tags/SEAndroid/"/>
    
      <category term="SELinux" scheme="http://yoursite.com/tags/SELinux/"/>
    
  </entry>
  
  <entry>
    <title>Android内核剖析之阅读记录</title>
    <link href="http://yoursite.com/Android-%E5%86%85%E6%A0%B8%E5%89%96%E6%9E%90/"/>
    <id>http://yoursite.com/Android-内核剖析/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-16T12:33:49.161Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>《Android内核剖析》–柯元旦<br>偏原理，2011年电子工业出版社出版</p><h1 id="第一章：Linux基础知识"><a href="#第一章：Linux基础知识" class="headerlink" title="第一章：Linux基础知识"></a>第一章：Linux基础知识</h1><p>1.1 Linux文件系统<br>1.2 Linux启动过程<br> Android对应的uboot/fastboot<br>1.3 常用Linux命令<br> export：用于将每个变量值的作用域设为全局范围<br> example: Terminal 字符串名字太长，export PS1=me<br>1.4 shell脚本备忘<br> 脚本本身不代表任何计算机语言，也不属于计算机语言范畴，到目前为止，人们把那种不需要编译的程序文件称之为脚本.<br> 不同脚本需要不同的解释器来解释执行.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#! bin/bash</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"Hello World"</span> <span class="comment"># show a message</span></span><br></pre></td></tr></table></figure></p><p>1.5 Make脚本备忘<br> Make脚本多用于自动编译过程，具体可以参考GNU Make</p><h1 id="第二章：Java基础"><a href="#第二章：Java基础" class="headerlink" title="第二章：Java基础"></a>第二章：Java基础</h1><p>2.1 类装载器 DexClassLoader<br>动态装载Class文件的ClassLoader类.在Java中使用.<br>DexClassLoader是在Android中使用，用来装载Dex文件.<br>2.2 JNI 调用机制<br>Java Native Interface<br>2.2.1 Java 访问 C<br>2.2.2 C 访问 Java<br>C 调用Java只能在Java调用C中进行.<br>2.2.3 在C中使用持久对象<br>2.3 异步消息处理线程<br> Android中的异步消息处理机制：Message、MessageQueue、Looper、Handler<br> Looper还有一个特殊的概念，那就是ThreadLocal，ThreadLocal并不是线程，它的作用是在每个线程中存储数据.<br> Handler的主要任务是将一个任务切换到指定的线程中执行.</p><h1 id="第三章：Android源码下载以及开发环境的配置"><a href="#第三章：Android源码下载以及开发环境的配置" class="headerlink" title="第三章：Android源码下载以及开发环境的配置"></a>第三章：Android源码下载以及开发环境的配置</h1><p> 有Eclipse中导入Android源码的方法</p><h1 id="第四章：使用Git"><a href="#第四章：使用Git" class="headerlink" title="第四章：使用Git"></a>第四章：使用Git</h1><h1 id="第五章：Binder"><a href="#第五章：Binder" class="headerlink" title="第五章：Binder"></a>第五章：Binder</h1><p> Binder用于进程间通讯，英文的意思是别针、回形针.<br> 由四个部分组成：Binder驱动、ServiceManager、Binder服务端、Binder客户端</p><ol><li>Binder驱动</li><li>ServiceManager</li><li>Binder服务端</li><li>Binder客户端<br><a href="https://blog.csdn.net/freekiteyu/article/details/70082302" target="_blank" rel="noopener">Android-Binder进程间通讯机制</a></li></ol><p>第六章：Framework概述<br>分服务端、客户端和Linux驱动端<br>服务端-WMS、AMS<br>客户端-<br>Linux驱动端-SurfaceFlinger和Binder<br>APK的启动过程<br>每个Activity最少有三个线程<br>第七章：理解Context<br>第八章：窗口创建过程  讲到了Token<br>第九章：Framework的启动过程<br>讲述了Framework的启动过程<br>第十章：AMS内部原理  &lt;重点&gt;<br>第十一章：从输入设备中获取消息<br>第十二章：屏幕绘制基础–SurfaceFlinger<br>第十三章：View工作原理 &lt;重点&gt;<br>第十四章：WMS工作原理 &lt;重点&gt;<br>第十五章：</p><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;《Android内核剖析》–柯元旦&lt;br&gt;偏原理，2011年电子工业出版社出版&lt;/p&gt;
&lt;h1 id=&quot;第一章：Linux基础知识&quot;&gt;&lt;a 
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="内核剖析" scheme="http://yoursite.com/categories/Android/%E5%86%85%E6%A0%B8%E5%89%96%E6%9E%90/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="内核剖析" scheme="http://yoursite.com/tags/%E5%86%85%E6%A0%B8%E5%89%96%E6%9E%90/"/>
    
  </entry>
  
  <entry>
    <title>Android中BroadcastRecevier解析</title>
    <link href="http://yoursite.com/Android-BroadcastRecevier-Analysis/"/>
    <id>http://yoursite.com/Android-BroadcastRecevier-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-08T02:29:07.413Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><ol><li>Android Broadcast广播注册和发布方式以及机制流程 <a href="https://blog.csdn.net/liu_3262296/article/details/51935316" target="_blank" rel="noopener">https://blog.csdn.net/liu_3262296/article/details/51935316</a></li><li>Android广播之发送广播的源码分析 <a href="https://blog.csdn.net/zhangyongfeiyong/article/details/52022935" target="_blank" rel="noopener">https://blog.csdn.net/zhangyongfeiyong/article/details/52022935</a></li><li>Android广播机制实现源码浅析（一） <a href="https://blog.csdn.net/hehui1860/article/details/30726609" target="_blank" rel="noopener">https://blog.csdn.net/hehui1860/article/details/30726609</a></li><li>Android广播机制实现源码浅析（二） <a href="https://blog.csdn.net/hehui1860/article/details/30727075" target="_blank" rel="noopener">https://blog.csdn.net/hehui1860/article/details/30727075</a></li><li>Android广播机制实现源码浅析（三）<a href="https://blog.csdn.net/hehui1860/article/details/30727537" target="_blank" rel="noopener">https://blog.csdn.net/hehui1860/article/details/30727537</a></li><li>BroadcastReceiver源码解析(一)  <a href="https://blog.csdn.net/tiefeng0606/article/details/51354381" target="_blank" rel="noopener">https://blog.csdn.net/tiefeng0606/article/details/51354381</a></li><li>BroadcastReceiver源码解析（二） <a href="https://blog.csdn.net/tiefeng0606/article/details/51381221" target="_blank" rel="noopener">https://blog.csdn.net/tiefeng0606/article/details/51381221</a></li></ol><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6730748" target="_blank" rel="noopener">Android系统中的广播（Broadcast）机制简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6737352" target="_blank" rel="noopener">Android应用程序注册广播接收器（registerReceiver）的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6744448" target="_blank" rel="noopener">Android应用程序发送广播（sendBroadcast）的过程分析</a></p><p><a href="https://blog.csdn.net/gaugamela/article/details/53515680" target="_blank" rel="noopener">Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;Android Broadcast广播注册和发布
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="BroadcastRecevier" scheme="http://yoursite.com/categories/Android/BroadcastRecevier/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="BroadcastRecevier" scheme="http://yoursite.com/tags/BroadcastRecevier/"/>
    
  </entry>
  
  <entry>
    <title>Android中启动过程解析</title>
    <link href="http://yoursite.com/Android-Boot-Process/"/>
    <id>http://yoursite.com/Android-Boot-Process/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-16T09:49:08.993Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6768304" target="_blank" rel="noopener">Android系统进程Zygote启动过程的源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6747696" target="_blank" rel="noopener">Android应用程序进程启动过程的源代码分析</a></p><p>Android开机流程概述：<br><a href="https://blog.csdn.net/h655370/article/details/77727554" target="_blank" rel="noopener">https://blog.csdn.net/h655370/article/details/77727554</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="启动" scheme="http://yoursite.com/categories/Android/%E5%90%AF%E5%8A%A8/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Zegote" scheme="http://yoursite.com/tags/Zegote/"/>
    
      <category term="Init" scheme="http://yoursite.com/tags/Init/"/>
    
  </entry>
  
  <entry>
    <title>Kaios</title>
    <link href="http://yoursite.com/Kaios/"/>
    <id>http://yoursite.com/Kaios/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-03T14:03:59.327Z</updated>
    
    <content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>官方网站</p><p><a href="https://www.kaiostech.com/" target="_blank" rel="noopener">Kaios官网</a></p><p><a href="https://developer.kaiostech.com/" target="_blank" rel="noopener">Kaios开发者官网</a></p><p><a href="https://developer.mozilla.org/zh-CN/" target="_blank" rel="noopener">火狐开发者官网</a></p><p><a href="https://developer.kaiostech.com/environment-setup" target="_blank" rel="noopener">开发环境配置</a></p><p><a href="https://wiki.mozilla.org/Firefox_OS/TCP" target="_blank" rel="noopener">Firefox OS/TCP</a> ★★★★★</p><p><a href="https://github.com/mozilla-b2g/B2G.git" target="_blank" rel="noopener">Kaios源码</a></p><p><a href="https://www.androidcentral.com/firefox-os" target="_blank" rel="noopener">Firefox OS</a></p><p><a href="https://developer.mozilla.org/en-US/docs/Archive/B2G_OS" target="_blank" rel="noopener">B2G OS</a> ★★★★★</p><p><a href="https://developer.mozilla.org/zh-CN/docs/Archive/B2G_OS/Platform/Architecture" target="_blank" rel="noopener">Firefox OS架构</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Archive/B2G_OS/Simulator" target="_blank" rel="noopener">Firefox OS 模拟器</a></p><p><a href="https://hacks.mozilla.org/category/firefox-os/" target="_blank" rel="noopener">Firefox OS Articles</a></p><p><a href="https://www.xda-developers.com/tag/firefox-os/" target="_blank" rel="noopener">FIREFOX OS Posts on XDA</a></p><h2 id="国内开发-Kaios"><a href="#国内开发-Kaios" class="headerlink" title="国内开发 Kaios"></a>国内开发 Kaios</h2><p><a href="https://blog.csdn.net/chieftain01/article/details/21450707" target="_blank" rel="noopener">Firefox OS简介</a></p><p><a href="https://blog.csdn.net/alwaysxihe/article/details/45561559" target="_blank" rel="noopener">Mozilla Firefox os系统构架详解</a></p><p><a href="https://blog.csdn.net/nxh_love/article/details/10610197" target="_blank" rel="noopener">Firefox OS 学习——学习目录</a><br><a href="https://blog.csdn.net/nxh_love/article/category/1524457" target="_blank" rel="noopener">Firefox OS 学习</a><br><a href="https://blog.csdn.net/nxh_love/article/details/9467707" target="_blank" rel="noopener">Firefox OS 学习——B2G 源码获取和build code</a></p><p><a href="https://blog.csdn.net/YAJUN0601/article/details/8620889" target="_blank" rel="noopener">FirefoxOS 系统进程初步分析 底层系统继承自 android</a></p><p><a href="https://blog.csdn.net/zembers/article/details/48495627" target="_blank" rel="noopener">Firefox OS启动过程分析－b2g进程启动</a></p><p><a href="https://blog.csdn.net/xiaowublog/article/details/21822917" target="_blank" rel="noopener">Firefox OS手机应用开发（三）：第一个Firefox OS应用程序</a></p><p><a href="https://blog.csdn.net/u013935416/article/details/25145239" target="_blank" rel="noopener">Firefox OS 2.0 模拟器 – 使用官方火狐浏览器扩展无痛完美模拟体验火狐手机系统</a></p><p><a href="https://blog.csdn.net/WY16223864/article/details/8299245" target="_blank" rel="noopener">Firefox OS编译</a><br>编译的时候遇到一个问题,报错<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Install dir: out/target/product/msm8909_q11/system/b2g</span><br><span class="line">rm -rf out/target/product/msm8909_q11/system/b2g/defaults</span><br><span class="line">mkdir -p out/target/product/msm8909_q11/system/b2g/defaults/pref</span><br><span class="line">cp -r /home/zds/Code/Q11/gaia/profile/defaults/* out/target/product/msm8909_q11/system/b2g/defaults/</span><br><span class="line">cp: cannot <span class="built_in">stat</span> `/home/zds/Code/Q11/gaia/profile/defaults/*<span class="string">': No such file or directory</span></span><br><span class="line"><span class="string">make: *** [out/target/product/msm8909_q11/system/gecko] Error 1</span></span><br></pre></td></tr></table></figure></p><p>查找发现解压有问题.到gaia目录手动把profile.tar.gz解压就行.就可以编译过</p><p>高通平台的话单模块编译，可以直接make 模块名</p><p><a href="https://git.kaiostech.com/" target="_blank" rel="noopener">Kaios</a> <!-- 账号： ganxiao@hymost.com 密码： hymostsw  --></p><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h1&gt;&lt;p&gt;官方网站&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.kaiostech.com/&quot; target=&quot;_blank&quot; rel=&quot;
      
    
    </summary>
    
      <category term="Kaios" scheme="http://yoursite.com/categories/Kaios/"/>
    
      <category term="Firefox OS" scheme="http://yoursite.com/categories/Kaios/Firefox-OS/"/>
    
    
      <category term="Kaios" scheme="http://yoursite.com/tags/Kaios/"/>
    
      <category term="Firefox OS" scheme="http://yoursite.com/tags/Firefox-OS/"/>
    
  </entry>
  
  <entry>
    <title>RxJava/RxAndroid</title>
    <link href="http://yoursite.com/RxJava-RxAndroid/"/>
    <id>http://yoursite.com/RxJava-RxAndroid/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-18T13:22:12.844Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://gank.io/post/560e15be2dca930e00da1083" target="_blank" rel="noopener">RxJava</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="RxJava" scheme="http://yoursite.com/categories/RxJava/"/>
    
      <category term="RxAndroid" scheme="http://yoursite.com/categories/RxJava/RxAndroid/"/>
    
    
      <category term="RxJava" scheme="http://yoursite.com/tags/RxJava/"/>
    
      <category term="RxAndroid" scheme="http://yoursite.com/tags/RxAndroid/"/>
    
  </entry>
  
  <entry>
    <title>Android中EventBus解析</title>
    <link href="http://yoursite.com/Android-EventBus/"/>
    <id>http://yoursite.com/Android-EventBus/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-18T13:38:26.810Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://xudeveloper.github.io/2018/01/20/EventBus%203.1.1%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/" target="_blank" rel="noopener">EventBus 3.1.1 源码解析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="EventBus" scheme="http://yoursite.com/categories/Android/EventBus/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="EventBus" scheme="http://yoursite.com/tags/EventBus/"/>
    
  </entry>
  
  <entry>
    <title>区块链开发</title>
    <link href="http://yoursite.com/Block-Chain/"/>
    <id>http://yoursite.com/Block-Chain/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-30T07:07:51.247Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://juejin.im/entry/5a95511cf265da4e7d6070d7?utm_source=gold_browser_extension" target="_blank" rel="noopener">区块链技术开发相关资料 - 介绍、教程、项目、资料、应用</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="区块链" scheme="http://yoursite.com/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="BlockChain" scheme="http://yoursite.com/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/BlockChain/"/>
    
    
      <category term="区块链" scheme="http://yoursite.com/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="BlockChain" scheme="http://yoursite.com/tags/BlockChain/"/>
    
  </entry>
  
  <entry>
    <title>AliOS</title>
    <link href="http://yoursite.com/AliOS/"/>
    <id>http://yoursite.com/AliOS/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-09-06T10:09:19.925Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p><a href="https://www.yunos.com/" target="_blank" rel="noopener">官网</a></p><p>产品<br><a href="https://www.yunos.com/product?spm=a211jy.11095046.7172290978.2.425c67a42fZhJt" target="_blank" rel="noopener">AliOS</a><br>应用与 Auto，Phone，TV，Wear<br><a href="https://www.yunos.com/things?spm=a211jy.11094992.6772079555.3.ef7a98d6082YzM" target="_blank" rel="noopener">AliOS Things</a></p><p>解决方案全景图<br><img src="/AliOS/quanjintu.png" alt="&quot;解决方案全景图&quot;"></p><p><a href="https://github.com/alibaba/AliOS-Things" target="_blank" rel="noopener">AliOS Things 开源代码</a></p><p><a href="https://www.aliyun.com/jiaocheng/topic_14282.html" target="_blank" rel="noopener">YunOS 教程中心</a></p><h1 id="关于AliOS"><a href="#关于AliOS" class="headerlink" title="关于AliOS"></a>关于AliOS</h1><p><a href="https://blog.csdn.net/yazhouren/article/details/78698659" target="_blank" rel="noopener">吐槽GitHub上刚放出来的阿里AliOS物联网操作系统，顺便聊下原创一条龙的重要性</a><br><a href="http://www.chinaz.com/news/2015/1118/471858.shtml" target="_blank" rel="noopener">为何 YunOS 有别于 Android？你需要知道的全都在这里</a></p><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://www.yunos.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;官网&lt;/a&gt;
      
    
    </summary>
    
      <category term="AliOS" scheme="http://yoursite.com/categories/AliOS/"/>
    
      <category term="YunOS" scheme="http://yoursite.com/categories/AliOS/YunOS/"/>
    
    
      <category term="AliOS" scheme="http://yoursite.com/tags/AliOS/"/>
    
      <category term="YunOS" scheme="http://yoursite.com/tags/YunOS/"/>
    
  </entry>
  
  <entry>
    <title>Android中Handler解析</title>
    <link href="http://yoursite.com/Android-Handler-Analysis/"/>
    <id>http://yoursite.com/Android-Handler-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-16T09:48:49.167Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6817933" target="_blank" rel="noopener">Android应用程序消息处理机制（Looper、Handler）分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6882903" target="_blank" rel="noopener">Android应用程序键盘（Keyboard）消息处理机制分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6905587" target="_blank" rel="noopener">Android应用程序线程消息循环模型分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Handler" scheme="http://yoursite.com/categories/Android/Handler/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Handler" scheme="http://yoursite.com/tags/Handler/"/>
    
  </entry>
  
  <entry>
    <title>C++ 基础</title>
    <link href="http://yoursite.com/C-Base/"/>
    <id>http://yoursite.com/C-Base/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-20T01:41:34.102Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="http://www.runoob.com/cplusplus/cpp-tutorial.html" target="_blank" rel="noopener">C++菜鸟教程</a><br><a href="https://www.w3cschool.cn/cpp/" target="_blank" rel="noopener">W3-C++教程</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="C++" scheme="http://yoursite.com/categories/C/"/>
    
      <category term="Base" scheme="http://yoursite.com/categories/C/Base/"/>
    
    
      <category term="Base" scheme="http://yoursite.com/tags/Base/"/>
    
      <category term="C++" scheme="http://yoursite.com/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>英语单词</title>
    <link href="http://yoursite.com/English-Word/"/>
    <id>http://yoursite.com/English-Word/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-09-09T09:57:04.661Z</updated>
    
    <content type="html"><![CDATA[<p>noun 名词 –n<br>adjective 形容词  –adj<br>adverb   副词  –adv</p><h1 id="2018-08-21"><a href="#2018-08-21" class="headerlink" title="2018-08-21"></a>2018-08-21</h1><p>determine 下决心<br>chubby 胖<br>altitude 高度<br>latitude 纬度<br>make up one’s mind 做决定，下决心<br>Think about it before you make up your mind.  在决定之前要先想一想<br>give in 投降<br>give up 放弃<br>atlas 地图集 a book of maps<br>glacier 冰川<br>grocer 食品杂货商，杂货店<br>laser 激光，激光器<br>rapids 急流，湍流  n.<br>No boat could pass through these rapids and survive.<br>rapid 迅速的，急促的，急流，adj.<br>The rabbit ran at a rapid speed.<br>rapidly 迅速地，很快地，立即，adv.<br>The water here is flowing very rapidly.<br>vapid  索然无味的，无生气的，乏味的<br>I’m bored - is this book vapid, or are we ?<br>aids 艾滋病 Acquired Immune Deficiency Synd<br>This ribbon(红丝带) is to help people understand the AIDS disease.<br>rabies 狂犬病<br>A bite from a wild dog may result in rabies.<br>valley 山谷,溪谷，低谷<br>From the top of the mountain I could see the entire valley below.<br>alley 小巷，小径<br>The narrow alley winds through the old town. 这条窄巷弯弯曲曲的穿过古老的小镇.<br>ballet 芭蕾舞<br>The man is watching the ballet dance.<br>wallet 钱包，皮甲<br>My wallet holds all of my money and credit cards.<br>value 给…定价，评价，重视，珍视，价值，有用性<br>Which do you value more - the economy or the environment?<br>waterfall 瀑布<br>The stream drops over the boulders in a beautiful small waterfall.<br>watercolor 水彩，水彩画<br>Watercolor painiting is a fun way to pass the time.<br>watery 水的，水分多的，平淡的，稀薄的，松软的，水淋淋的<br>Look into my watery eyes and tell me I’m the cutest one!<br>watermelon 西瓜<br>The zombie(僵尸) was attacked by watermelon throwing plants.<br>after all 毕竟，终究<br>You should apologize to her. After all, she is your wife.<br>pace 步，步伐<br>The horse walked at a fast pace.<br>lace 花边，鞋带<br>Before you run,make sure your laces are tight.<br>pause 暂停，中止，停顿<br>The video pause at the moment he showed a funny face.<br>sauce 酱，调味汁，莽撞，冒失<br>He is adding different sauces onto the burger.<br>pence 便士<br>In th U.K., one hundred pence make one pound sterling. 在英国，一百便士等于一英镑.<br>bend 弯曲，倾向，屈服<br>First bend to the right, then bend to the left.<br>blender 掺和器，搅拌机<br>I feel like I’m getting into a blender, when I walk to my office.<br>bleed 流血，感到疼痛，榨取，敲诈<br>“I may bleed to death.”were the waterlemon’s last words.<br>meander 漫步<br>I like to meander through the forest.<br>delta 三角洲 = a pieces of land shaped like a triangle that is formed when a river splits into smaller rivers before it flows into an ocean<br>A thriving city was built at the river delta.<br>delete 删除<br>The man with tears on face deleted his ex-wife’s phone number.<br>attitude 态度<br>The waiter has such a bad attitude!<br>altitude 高度，海拔<br>At this altitude it would be dangerous to fall off. 在这种高度下跌落很危险.<br>latitude 纬度<br>Canada and Russia have similar altitude.<br>solitude 孤独，寂寞，荒野<br>I am in solitude. My shadow is my only friend.<br>antique 古董，古玩，古物<br>I’m fascinated by that antique at first sight!<br>boil 沸腾<br>As the water boils it gives off steam.<br>boiler 锅炉，煮器<br>The boiler takes up too much room in the bathroom.we should move it.<br>bill 账单，纸币，钞票<br>Could we have the bill, please? 请把账单给我们看一下.<br>bull 公牛，雄性的鲸，象等大动物<br>A bull market is a situation in which share prices are rising.<br>bell 钟，铃，门铃，钟声<br>He rang the bell to tell the waitress to pick up the dishes.<br>lorry 货车<br>All the stuff was carried in the lorry.<br>merry 欢乐的，愉快的<br>I wish you a merry Christmas!<br>Texas 德克萨斯州<br>Texas is the second largest American State.<br>text 文本，正文，原文，课本，课文，发送，短信<br>The text in this book is small and hard to read.<br>exam 考试<br>The students are taking an exam.<br>tease 取笑，嘲弄，挑逗，戏弄<br>All the kids at school teased him for being different.<br>nexus 关系，连接<br>I keep in close contact with my nexus of good friends.<br>tunas 金枪鱼<br>They caught a whole net full of fresh tunas. 他们捕到了满满一渔网的金枪鱼.<br>accent 口音<br>The foreigner has such a funny accent when he speaks Chinese!<br>absent 缺席的，不存在的，心不在焉的，出神的.<br>He listened to the lecture with an absent mind. 他上课也心不在焉.<br>pronunciation 发音，发音方法<br>He learns pronunciations of words from that book.<br>payment 支付，报酬<br>The clever dog made the payment for the bone itself.<br>accident 意外<br>Santa Claus is badly injured in the car accident. 圣诞老公公在车祸中受伤严重.<br>catfish 鲶鱼<br>Catfish hava whiskers just like cats do! 鲶鱼有猫一样的胡须.<br>Scottish 苏格兰人<br>These Scottish men are playing traditional music and wearing traditional clothes.<br>catch 捕捉，接住，赶上<br>Tha cat is happy to catch the mouse.<br>cash 现金，兑现<br>Please pay me with cash.<br>goldfish<br>A goldfish is a common pet.</p><h1 id="2018-9-9"><a href="#2018-9-9" class="headerlink" title="2018-9-9"></a>2018-9-9</h1><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;noun 名词 –n&lt;br&gt;adjective 形容词  –adj&lt;br&gt;adverb   副词  –adv&lt;/p&gt;
&lt;h1 id=&quot;2018-08-21&quot;&gt;&lt;a href=&quot;#2018-08-21&quot; class=&quot;headerlink&quot; title=&quot;2018-08-21
      
    
    </summary>
    
      <category term="English" scheme="http://yoursite.com/categories/English/"/>
    
      <category term="word" scheme="http://yoursite.com/categories/English/word/"/>
    
    
      <category term="English" scheme="http://yoursite.com/tags/English/"/>
    
      <category term="word" scheme="http://yoursite.com/tags/word/"/>
    
  </entry>
  
  <entry>
    <title>Fuchsia</title>
    <link href="http://yoursite.com/Fuchsia/"/>
    <id>http://yoursite.com/Fuchsia/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-05T08:42:47.609Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Fuchsia"><a href="#Fuchsia" class="headerlink" title="Fuchsia"></a>Fuchsia</h1><p><a href="https://fuchsia.googlesource.com/" target="_blank" rel="noopener">Fuchsia官网</a></p><p><a href="https://github.com/zhangpf/fuchsia-docs-zh-CN" target="_blank" rel="noopener">Google Fuchsia文档的简体中文翻译</a> ★★★★★</p><p><a href="https://github.com/fuchsia-mirror" target="_blank" rel="noopener">Fuchsia项目在github的镜像</a></p><p>Fuchsia涉及到的开发语言： C   C++   Go   Python   Rust  Dart</p><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://www.toutiao.com/a6583532670842569219/" target="_blank" rel="noopener">Android 将死，Fuchsia 当立？</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Fuchsia&quot;&gt;&lt;a href=&quot;#Fuchsia&quot; class=&quot;headerlink&quot; title=&quot;Fuchsia&quot;&gt;&lt;/a&gt;Fuchsia&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://fuchsia.googlesource.com/&quot; target
      
    
    </summary>
    
      <category term="Fuchsia" scheme="http://yoursite.com/categories/Fuchsia/"/>
    
      <category term="Flutter" scheme="http://yoursite.com/categories/Fuchsia/Flutter/"/>
    
    
      <category term="Fuchsia" scheme="http://yoursite.com/tags/Fuchsia/"/>
    
      <category term="Flutter" scheme="http://yoursite.com/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Git and Repo</title>
    <link href="http://yoursite.com/Git-Repo/"/>
    <id>http://yoursite.com/Git-Repo/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-05T09:07:37.229Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://git-scm.com/doc" target="_blank" rel="noopener">Git官网</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Git" scheme="http://yoursite.com/categories/Git/"/>
    
      <category term="Repo" scheme="http://yoursite.com/categories/Git/Repo/"/>
    
    
      <category term="Git" scheme="http://yoursite.com/tags/Git/"/>
    
      <category term="Repo" scheme="http://yoursite.com/tags/Repo/"/>
    
  </entry>
  
  <entry>
    <title>给技术人员一些技术以外的建议</title>
    <link href="http://yoursite.com/IT-Advice-1/"/>
    <id>http://yoursite.com/IT-Advice-1/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-10T02:48:13.348Z</updated>
    
    <content type="html"><![CDATA[<p>做 IT 也有好些年了，中间踩过很多坑，踩完坑之后也有不少收获。有些东西只有自己经历过才会印象深刻，不废话，先记录以下这些吧，后续想到再更新。</p><h1 id="需要及时反馈"><a href="#需要及时反馈" class="headerlink" title="需要及时反馈"></a>需要及时反馈</h1><p>举个例子，如果你手头有一个事情需要别人去完成。当你告诉别人这个需求的时候，很多情况下是希望那个人能够给你完成时间点的，而不是简单的告诉你会去做。更好的情况是，当你给出需求的时候，那个实现者能够提供出更好的方案，或者能够和你一起分析实现过程中可能遇到的问题，解决的大致计划，最后给到你一个完成的时间点，顺带说明下可能存在的风险。这样的回复，你会觉得这个人做事是靠谱的，是真正理解了你的需求去做的，而不是拿到需求后放在那里应付一下或者排计划(排计划最好也给出一个能够制定计划的时间点)，需要不断地 push 才能做。</p><p>工程师或者技术人员作为实现方，承接很多来自产品、运营、甚至市场的需求。这些需求当中，有些可以明确的确定相应的技术方案，有些需要调研，有些可能现阶段技术上还不够成熟。无论是以上哪种情况，在接受需求的那一刻，你需要及时的反馈一些信息给需求方。比如预计完成的时间，可能会遇到的困难。让需求方能够经常性的知道事情的进展情况。这个就好比我们网上买完东西后，会经常性的去查询当前货物的物流情况，大概什么时候能到。所以换位思考，当提了一个需求给你的时候，你也需要经常性的将这个信息反馈给需求方，让他实时了解进展。</p><h1 id="注重团队合作"><a href="#注重团队合作" class="headerlink" title="注重团队合作"></a>注重团队合作</h1><p>现在的软件开发，是一个分工合作，协同作战的过程。像张小龙一个人写出 foxmail 的年代已经一去不复返了。在开发的时候你需要将自己作为团队的一份子，互相合作。当自己遇到问题的时候，及时的抛出来给团队，而不是一个人在那边死磕。记住，你的背后是一个团队，你和他们在一起战斗。</p><p>在这个大规模协作的时代，一个人想要做出类似前辈们那种靠着单打独斗就做出类拔萃的成果已经变得极其困难了。这样的人，更多的时候是以领导者或者组织者的身份出现，但是真正的成绩都是靠背后的团队创造的。现在的产品想要成功，除了产品本身要可用、易用之外，很大程度上还依赖产品背后的运营、市场、投放、推广、数据挖掘、黑客增长等等方面。酒香不怕巷子深在现在这个快节奏的时代已经成为了历史。一个优秀的团队，是一个产品成功的必要条件。让自己融入优秀的团队，成为团队的一份子，荣辱与共，共同解决困难，一起分享喜悦。</p><h1 id="专业人做专业事"><a href="#专业人做专业事" class="headerlink" title="专业人做专业事"></a>专业人做专业事</h1><p>现在的社会分工很细，自己不懂的领域，找专业的人来解决。技术人有个特点是很多的事情喜欢刨根问底，追求知道所有的技术细节，甚至恨不得所有的东西都自己从头搞一遍。在现代软件规模下，自己搞完全是不现实的做法。术业有专攻，一个不专业的人去做一件事情，无论从时间成本还是经济成本肯定是不如专业人士，而且除此之外，往往还有看不见的成本，比如人的能力。即使时间充裕，资金充足，最终出来的产品也未必能够超越专业的。所以除了核心部分，其他的就交给专业人士去做吧。</p><h1 id="做事要讲究效率"><a href="#做事要讲究效率" class="headerlink" title="做事要讲究效率"></a>做事要讲究效率</h1><p>在外界看来，互联网有个特点就是加班多。996，997 也经常被周围的人提起。从效率上来说，程序员有效的工作时间也就那么几个小时。程序员是一种创造性的活动，需要的是灵感，而不是靠时间堆积的。所以更多的时候是需要找到自己效率最高的哪几个小时。在哪几个小时里去提高自己的生产力，而不是磨洋工。</p><p>还有一点，随着产品开发的节奏越来越快，手头的事情也会越来越多。永远不要想着把所有的事情全部处理完，总是有一些重要的，紧急的事情需要去处理。那些不重要和不紧急的事情，就随它去吧。大部分时间专注于那些重要不紧急的事情，才能避免经常处理重要紧急的事情。</p><h1 id="做好自己，再说别人"><a href="#做好自己，再说别人" class="headerlink" title="做好自己，再说别人"></a>做好自己，再说别人</h1><p>家里小朋友现在做一些事情，比如看电视，或者玩手机，我们都会有限制，而小朋友张口的理由就是爸爸妈妈也看电视和玩手机。比起看电视或者玩手机，被批评的时候总是先从别人身上找缺点这一点反而问题更严重。这就好比我们经常听到或者自己感受到的，一些电动车闯红灯，被交警抓到的时候可能首先会说你看前面谁骑电动车也闯红灯了，为什么没事，觉得自己心里不平衡。甚至说你看行人也闯红灯，为什么不用罚。</p><p>这里面其实我们往往忽视了一个重要的点：就是我们做这件事情的时候本身就是不对的，既然不对为什么不从自身先找找原因。我们总是乐于积极搜索别人的短处和自己的长处。而自动忽视自己不好的地方。做好自己，绝不是一件容易的事情。自己没做好，其实是没理由说别人的。但是反过来，别人没做好，没理由说自己，这就强词夺理了。</p><h1 id="凡事可以做的更好"><a href="#凡事可以做的更好" class="headerlink" title="凡事可以做的更好"></a>凡事可以做的更好</h1><p>这一条也算是对以上几条的补充。</p><p>大家都知道以前的汽车装配都是需要大量的工人来做的，很多装配工人成为熟练工之后就觉得装配是个体力活了。但是事实上，我们今天已经很少看到工厂里有大量的装配工人了。觉得体力活完全是认知问题，不代表做汽车装配这个没有提升的空间了，否则的话目前你去工厂可能还会看到大量的装配工人，而不会有现在流水线的装配作业了。有没有提升空间关键在于你是不是那个不安于现有的体力活，想把它做成流水线的那个装配工人。</p><p>有时候工程师业务做久了，业务层做的工作类似于装配工人做的事情，会觉得没有提升的空间或者提升的空间不大。其实大部分只是因为你用已有的认知思维用你熟悉的习惯方式做业务而已。拿到业务后，你会下意识的决定使用很多熟悉的技术方案，这些方案形成了你自己的舒适圈，你喜欢呆在这样的舒适圈中，没有想过去突破。渐渐的你就会觉得业务对你来说没有了挑战，觉得做着没意思。</p><p>其实写业务代码一样可以很牛逼，业务做多了之后，你需要思考的就是如何将业务封装和抽象出来，使业务代码可以更加方便的维护。或者从 SDK 的角度出发，如何让新人能够通过简单的文档就能实现业务。这些事情，同样都是业务层面的事情，但是在技术维度上却已经是两个完全不同的层次了。 有句广告词：没有最好，只有更好。同样的一件事情，做完和做好，结果上不一样，收获也不一样。</p><p>以上几点是平时工作中的一些感悟，自己有些也做的不好，拿出来分享给大家。也算是给自己一个警示，和大家一起共勉。</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://juejin.im/post/5b266c5ee51d45587d2dc67c?utm_source=gold_browser_extension" target="_blank" rel="noopener">给技术人员一些技术以外的建议</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;做 IT 也有好些年了，中间踩过很多坑，踩完坑之后也有不少收获。有些东西只有自己经历过才会印象深刻，不废话，先记录以下这些吧，后续想到再更新。&lt;/p&gt;
&lt;h1 id=&quot;需要及时反馈&quot;&gt;&lt;a href=&quot;#需要及时反馈&quot; class=&quot;headerlink&quot; title=&quot;需
      
    
    </summary>
    
      <category term="IT" scheme="http://yoursite.com/categories/IT/"/>
    
      <category term="建议" scheme="http://yoursite.com/categories/IT/%E5%BB%BA%E8%AE%AE/"/>
    
      <category term="Advice" scheme="http://yoursite.com/categories/IT/%E5%BB%BA%E8%AE%AE/Advice/"/>
    
    
      <category term="IT" scheme="http://yoursite.com/tags/IT/"/>
    
      <category term="建议" scheme="http://yoursite.com/tags/%E5%BB%BA%E8%AE%AE/"/>
    
      <category term="Advice" scheme="http://yoursite.com/tags/Advice/"/>
    
  </entry>
  
  <entry>
    <title>Android中ContentProvider解析</title>
    <link href="http://yoursite.com/Android-ContentProvider-Analysis/"/>
    <id>http://yoursite.com/Android-ContentProvider-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-16T09:48:52.886Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6946067" target="_blank" rel="noopener">Android应用程序组件Content Provider简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6950440" target="_blank" rel="noopener">Android应用程序组件Content Provider应用实例</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6963418" target="_blank" rel="noopener">Android应用程序组件Content Provider的启动过程源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6967204" target="_blank" rel="noopener">Android应用程序组件Content Provider在应用程序之间共享数据的原理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6985171" target="_blank" rel="noopener">Android应用程序组件Content Provider的共享数据更新通知机制分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;参考文献&quot;&gt;&lt;a href=&quot;#参考文献&quot; class=&quot;headerlink&quot; title=&quot;参考文献&quot;&gt;&lt;/a&gt;参考文献&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="ContentProvider" scheme="http://yoursite.com/categories/Android/ContentProvider/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="ContentProvider" scheme="http://yoursite.com/tags/ContentProvider/"/>
    
  </entry>
  
  <entry>
    <title>Android中按键事件传递分析</title>
    <link href="http://yoursite.com/Android-KeyEvent-Analysis/"/>
    <id>http://yoursite.com/Android-KeyEvent-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-20T00:57:20.922Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><p>Framework Native模块代码</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/services/core/jni/</span><br><span class="line">├── com_android_server_input_InputManagerService.cpp</span><br><span class="line">├── com_android_server_input_InputApplicationHandle.h</span><br><span class="line">├── com_android_server_input_InputApplicationHandle.c</span><br><span class="line">├── com_android_server_input_InputWindowHandle.h</span><br><span class="line">├── com_android_server_input_InputWindowHandle.c</span><br><span class="line">└──</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">frameworks/native/include/android</span><br><span class="line">├── input.h</span><br><span class="line">└── keycodes.h</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">frameworks/native/include/input</span><br><span class="line">├── DisplayViewport.h</span><br><span class="line">├── IInputFlinger.h</span><br><span class="line">├── InputDevice.h</span><br><span class="line">├── InputEventLabels.h</span><br><span class="line">├── Input.h</span><br><span class="line">├── InputTransport.h</span><br><span class="line">├── Keyboard.h</span><br><span class="line">├── KeyCharacterMap.h</span><br><span class="line">├── KeyLayoutMap.h</span><br><span class="line">├── VelocityControl.h</span><br><span class="line">├── VelocityTracker.h</span><br><span class="line">└── VirtualKeyMap.h</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">frameworks/native/services/inputflinger</span><br><span class="line">├── Android.bp</span><br><span class="line">├── EventHub.cpp</span><br><span class="line">├── EventHub.h</span><br><span class="line">├── host</span><br><span class="line">│   ├── Android.bp</span><br><span class="line">│   ├── InputDriver.cpp</span><br><span class="line">│   ├── InputDriver.h</span><br><span class="line">│   ├── InputFlinger.cpp</span><br><span class="line">│   ├── InputFlinger.h</span><br><span class="line">│   ├── inputflinger.rc</span><br><span class="line">│   ├── InputHost.cpp</span><br><span class="line">│   ├── InputHost.h</span><br><span class="line">│   └── main.cpp</span><br><span class="line">├── InputApplication.cpp</span><br><span class="line">├── InputApplication.h</span><br><span class="line">├── InputDispatcher.cpp</span><br><span class="line">├── InputDispatcher.h</span><br><span class="line">├── InputListener.cpp</span><br><span class="line">├── InputListener.h</span><br><span class="line">├── InputManager.cpp</span><br><span class="line">├── InputManager.h</span><br><span class="line">├── InputReader.cpp</span><br><span class="line">├── InputReader.h</span><br><span class="line">├── InputWindow.cpp</span><br><span class="line">├── InputWindow.h</span><br><span class="line">├── PointerControllerInterface.h</span><br><span class="line">└── tests</span><br><span class="line">    ├── Android.bp</span><br><span class="line">    ├── InputDispatcher_test.cpp</span><br><span class="line">    └── InputReader_test.cpp</span><br></pre></td></tr></table></figure><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：Android按键事件传递流程(一)    时间：2018/07/19--><p>[1095/1219] including ./vendor/qcom/proprietary/diag/Android.mk …<br>build/core/binary.mk:1522: error: vendor/qcom/proprietary/diag/diag_ep/Android.mk: diag_ep: C_INCLUDES must be under the source or output directories: /diag/diag_ep /diag/include /diag/src.</p><p>用户态</p><p>这两篇文章关注了很多的代码细节：<br><a href="https://blog.csdn.net/w690333243/article/details/77493981" target="_blank" rel="noopener">Android按键事件传递流程(一)</a><br><a href="https://blog.csdn.net/qidabing/article/details/74073515" target="_blank" rel="noopener">Android按键事件传递流程(二)</a></p><p>将Native层的Input子系统，起到承上启下的功能.连接Kernel 和 Fwk<br><a href="https://www.cnblogs.com/haiming/p/3318614.html" target="_blank" rel="noopener">Android Framework——之Input子系统</a></p><p>内核态<br><a href="https://www.jianshu.com/p/4f59efee9cd8" target="_blank" rel="noopener">Android物理按键输入事件(一)</a><br><a href="https://www.jianshu.com/p/3fe41b6d0c92" target="_blank" rel="noopener">Android物理输入事件(二)</a><br><a href="https://blog.csdn.net/u013308744/article/details/49274069" target="_blank" rel="noopener">android kl 文件的作用</a></p><p><a href="https://blog.csdn.net/woliuyunyicai/article/details/48577617" target="_blank" rel="noopener">View机制深入学习（三） View中的消息传递及InputManagerService</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;p
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="KeyEvent" scheme="http://yoursite.com/categories/Android/KeyEvent/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="KeyEvent" scheme="http://yoursite.com/tags/KeyEvent/"/>
    
  </entry>
  
  <entry>
    <title>关于有潜力的程序员</title>
    <link href="http://yoursite.com/About-Good-Programmer/"/>
    <id>http://yoursite.com/About-Good-Programmer/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-09-10T06:13:50.973Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p> 本文收集程序员的故事</p><h1 id="文章列表"><a href="#文章列表" class="headerlink" title="文章列表"></a>文章列表</h1><p><a href="http://www.najiaoluo.com/jiaoyu/4915145.html" target="_blank" rel="noopener">面试过3000位候选人，我发现那些厉害的年轻人都有这3个特质</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt; 本文收集程序员的故事&lt;/p&gt;
&lt;h1 id=&quot;文章列表&quot;&gt;&lt;a href=&quot;#文章列表&quot; class=&quot;headerlink&quot; title
      
    
    </summary>
    
      <category term="Story" scheme="http://yoursite.com/categories/Story/"/>
    
    
      <category term="Story" scheme="http://yoursite.com/tags/Story/"/>
    
  </entry>
  
  <entry>
    <title>Android中Activity解析</title>
    <link href="http://yoursite.com/Android-Activity-Analysis/"/>
    <id>http://yoursite.com/Android-Activity-Analysis/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-08T05:59:22.924Z</updated>
    
    <content type="html"><![CDATA[<p>上次我们讲到Activity的四种启动模式的时候，已经了解到一些关于task的技术，今天我再向大家介绍一下。task是一个具有栈结构的容器，可以放置多个Activity实例。启动一个应用，系统就会为之创建一个task，来放置根Activity；默认情况下，一个Activity启动另一个Activity时，两个Activity是放置在同一个task中的，后者被压入前者所在的task栈，当用户按下后退键，后者从task被弹出，前者又显示在幕前，特别是启动其他应用中的Activity时，两个Activity对用户来说就好像是属于同一个应用；系统task和task之间是互相独立的，当我们运行一个应用时，按下Home键回到主屏，启动另一个应用，这个过程中，之前的task被转移到后台，新的task被转移到前台，其根Activity也会显示到幕前，过了一会之后，在此按下Home键回到主屏，再选择之前的应用，之前的task会被转移到前台，系统仍然保留着task内的所有Activity实例，而那个新的task会被转移到后台，如果这时用户再做后退等动作，就是针对该task内部进行操作了。</p><p>我们今天就讲一下和task相关的知识，主要分一下几点：</p><p>1.Activity的affinity(亲和力)</p><p>2.Intent几种常见的flags</p><p>3.与task相关属性</p><p>affinity：</p><p>task对于Activity来说就好像它的身份证一样，可以告诉所在的task，自己属于这个task中的一员；拥有相同affinity的多个Activity理论同属于一个task，task自身的affinity决定于根Activity的affinity值。affinity在什么场合应用呢？1.根据affinity重新为Activity选择宿主task（与allowTaskReparenting属性配合工作）；2.启动一个Activity过程中Intent使用了FLAG_ACTIVITY_NEW_TASK标记，根据affinity查找或创建一个新的具有对应affinity的task。我们会在后面进行详细讲解。</p><p>默认情况下，一个应用内的所有Activity都具有相同的affinity，都是从Application（参考的taskAffinity属性）继承而来，而Application默认的affinity是中的包名，我们可以为设置taskAffinity属性值，这样可以应用到下的所有，也可以单独为某个Activity设置taskAffinity。例如：在系统自带的Browser中，package为com.android.browser，但是却自定义一个taskAffinity属性值：</p><p>Ongoing,待完成，Please wait.</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6685853" target="_blank" rel="noopener">Android应用程序的Activity启动过程简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6689748" target="_blank" rel="noopener">Android应用程序启动过程源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6703247" target="_blank" rel="noopener">Android应用程序内部启动Activity过程（startActivity）的源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6720261" target="_blank" rel="noopener">Android应用程序在新的进程中启动新的Activity的方法和过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6714543" target="_blank" rel="noopener">解开Android应用程序组件Activity的”singleTask”之谜</a></p><p><a href="http://www.cnblogs.com/CSU-PL/p/3794280.html" target="_blank" rel="noopener">android的task任务栈</a></p><p><a href="https://www.jianshu.com/p/9ecea420eb52" target="_blank" rel="noopener">3分钟看懂Activity启动流程</a></p><p><a href="">Android 7.0 startActivity()源码解析以及对几个问题的思考</a></p><p><a href="https://blog.csdn.net/windskier/article/details/7096521" target="_blank" rel="noopener">android Application Component研究之Activity(一)</a><br><a href="https://blog.csdn.net/a19891024/article/details/54342799" target="_blank" rel="noopener">Android instrumentation原理</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;上次我们讲到Activity的四种启动模式的时候，已经了解到一些关于task的技术，今天我再向大家介绍一下。task是一个具有栈结构的容器，可以放置多个Activity实例。启动一个应用，系统就会为之创建一个task，来放置根Activity；默认情况下，一个Activit
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Activity" scheme="http://yoursite.com/categories/Android/Activity/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Activity" scheme="http://yoursite.com/tags/Activity/"/>
    
  </entry>
  
  <entry>
    <title>Cracking the Coding Interview</title>
    <link href="http://yoursite.com/Cracking-the-Coding-Interview/"/>
    <id>http://yoursite.com/Cracking-the-Coding-Interview/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-27T01:41:03.873Z</updated>
    
    <content type="html"><![CDATA[<p>程序员面试金典</p><p><a href="https://download.csdn.net/download/u011433684/9553267" target="_blank" rel="noopener">下载地址</a></p><p><a href="https://careers.google.com/intl/zh_cn/stories/applying-to-google/" target="_blank" rel="noopener">Google对实习生面试准备的推荐</a><br>里面提到了推荐阅读 Cracking the Coding Interview这个本书</p><p><a href="https://www.cnblogs.com/zhuli19901106/p/cracking-the-coding-interview.html" target="_blank" rel="noopener">《Cracking the Coding Interview》读书笔记</a></p><p>对于代码的编译运行测试</p><h1 id="在线编译运行小工具"><a href="#在线编译运行小工具" class="headerlink" title="在线编译运行小工具"></a>在线编译运行小工具</h1><p>适用于Php,Java,C,C++,Go等多种语言<br><a href="https://c.runoob.com/compile" target="_blank" rel="noopener">https://c.runoob.com/compile</a> 背景为白色<br><a href="https://tool.lu/coderunner/" target="_blank" rel="noopener">https://tool.lu/coderunner/</a>  背景为黑色</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;程序员面试金典&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://download.csdn.net/download/u011433684/9553267&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;ht
      
    
    </summary>
    
      <category term="InterView" scheme="http://yoursite.com/categories/InterView/"/>
    
      <category term="Code" scheme="http://yoursite.com/categories/InterView/Code/"/>
    
      <category term="C++" scheme="http://yoursite.com/categories/InterView/Code/C/"/>
    
    
      <category term="C++" scheme="http://yoursite.com/tags/C/"/>
    
      <category term="InterView" scheme="http://yoursite.com/tags/InterView/"/>
    
      <category term="Code" scheme="http://yoursite.com/tags/Code/"/>
    
  </entry>
  
  <entry>
    <title>Kotlin</title>
    <link href="http://yoursite.com/Kotlin/"/>
    <id>http://yoursite.com/Kotlin/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-10T03:20:31.093Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Kotlin-官方网站"><a href="#Kotlin-官方网站" class="headerlink" title="Kotlin 官方网站"></a>Kotlin 官方网站</h1><p><a href="https://developer.android.google.cn/kotlin/" target="_blank" rel="noopener">Kotlin </a><br><a href="https://android.github.io/kotlin-guides/" target="_blank" rel="noopener">Kotlin Home</a><br><a href="https://developer.android.google.cn/kotlin/resources" target="_blank" rel="noopener">Kotlin学习资源</a></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Kotlin-官方网站&quot;&gt;&lt;a href=&quot;#Kotlin-官方网站&quot; class=&quot;headerlink&quot; title=&quot;Kotlin 官方网站&quot;&gt;&lt;/a&gt;Kotlin 官方网站&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://developer.android
      
    
    </summary>
    
      <category term="Kotlin" scheme="http://yoursite.com/categories/Kotlin/"/>
    
    
      <category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
    
  </entry>
  
  <entry>
    <title>My Approach to Getting Dramatically Better as a Programmer</title>
    <link href="http://yoursite.com/L-My-Approach-to-Getting-Dramatically-Better-as-a-Programmer/"/>
    <id>http://yoursite.com/L-My-Approach-to-Getting-Dramatically-Better-as-a-Programmer/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-25T12:15:40.224Z</updated>
    
    <content type="html"><![CDATA[<!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><p>There was a recent discussion among my social group about what “getting dramatically better as a programmer” means. Based on that discussion, I’ve decided to share my own approach to becoming a “dramatically better programmer”. I want others to understand what practices I’ve found useful, so they can incorporate them into their own life.</p><p>My approach to getting dramatically better is built around a training regime. There are a specific set of “exercises” I do every week. I designed the training regime with two explicit goals in mind:</p><p>Learning how to solve problems I didn’t know how to solve before.<br>Learning how to write correct programs faster.<br>My training routine consists of a total of four different exercises. Each one helps me achieve the two objectives above. The four different exercises are:</p><p>Reading a paper.<br>Learning a new tool.<br>Reading several chapters of a book.<br>Recording my screen as I write a program. Then reviewing the footage and seeing how I could have written the program faster.<br>Let me explain each of the exercises in a bit more depth. I want to share some details on how each exercise works and the main benefits I’ve had from doing each exercise.</p><p>Reading a Paper<br>This exercise is designed to expand my knowledge of Computer Science. I’ve found two direct benefits to reading papers. The first benefit is that some papers have changed my model of certain problems. A good example of this is The Tail at Scale. That paper examines the counter intuitive nature of long tail latency.</p><p>One interesting lesson I learned from the paper was how running a request on multiple machines affects latency. The authors looked at empirical data from a Google service.  The service processes a request by distributing parts of the request across many different services. They used the data to estimate what would happen if you distributed requests across 100 different services. The authors found that if you measure the time it takes to get a response from all 100 severs, half of that time will be spent waiting for only the last five! This is because the slowest 5% of requests is that much slower than all the other requests. The paper also gives several approaches on how to reduce tail latency. I have found those approaches useful in my own work.</p><p>The other benefit I’ve found from reading papers is that they give me the knowledge to understand different systems as a whole. As an example, take Spanner, Google’s distributed database. Spanner uses many different techniques such as Paxos, two phase commit, MVCC, and predicate locks. I’ve been able to build up an understanding of these different techniques through reading papers. In turn, this enables me to reason the Spanner as a whole and reason about the trade offs Spanner makes compared to other systems.</p><p>I find most papers I read by either following the references of papers I have read or following up on a paper covered in the Morning Paper. The book Designing Data Intensive Applications also has a lot of references to papers that are worth reading.</p><p>Learning a New Tool<br>One of the easiest ways to solve problems is to use an existing tool that already solves that problem. For this exercise, I pick a tool and learn a bit about it. Usually I setup the tool locally, go through a few tutorials, and read a bit of the manual. Tools that I’ve learned in the past range from bash utils such as jq or sed to distributed systems such as Kafka or Zookeeper.</p><p>Learning the bash utilities helps me get solve many common tasks quicker than I could have otherwise. Simple text processing is often easier with sed than it is with a programming language. Likewise, learning about different distributed systems come in handy for understanding what tools work well for solving different problems. That way I know what tool I should use when faced with a given problem.</p><p>Reading Several Chapters of a Book<br>I use books to supplement the knowledge I don’t get from reading a paper or<br>learning a tool. The books I read cover a wide range of topics. Books I’ve read<br>recently include:</p><p>Refactoring – I found this to be a great way to understand what good code should look like and how to turn bad code into good code.<br>Getting Things Done – I found this book to be helpful for prioritizing and keeping track of things. It helped me build a system to make sure I get done what’s important for me to get done.<br>The First Time Manager – I recently became the team coordinator for my team at work. As team coordinator, my main responsibility is communicating with other teams when necessary. I also lead my team’s meetings. I found this book to be great for getting an understanding of basic management principles.<br>Recording My Screen<br>This exercise is my favorite. It’s the exercise that’s changed how I approach problems the most. It’s common for athletes to review footage of themselves to understand how they could have done better. I decided to apply the same approach to programming. Some of the lessons I’ve learned by recording my screen include:</p><p>It helps to test code as it’s written. Doing this reduces the amount of time it takes to debug code by reducing the time you spend locating where the bug is. If all your existing code is bug free, the bug has to be in the new code you just wrote.<br>When debugging a problem, adding functionality only for the purpose debugging is often well worth the cost. As an example, one toy problem I worked on was writing an LRU cache. I had a bug where it wasn’t evicting the right elements. I was able to quickly determine what was wrong by adding a function to print the state of the cache. I could then see where the expected behavior of the cache differed from the actual behavior. This allowed me to quickly locate the bug.<br>Spending five minutes deciding on an approach up front before writing any code is worth it. I’ve found two benefits to doing this. It helps me make sure my approach is correct. More importantly, it forces me to decide on a single approach. By watching a recording of myself, I found I wasted a lot of time switching my code between two different approaches. In reality, either approach would have worked fine.<br>All these lessons are obvious in retrospect. but I had no clue that any of these were issues until I recorded my screen and saw where I was actually spending time.</p><p>The steps I take for this exercise are:</p><p>Record myself writing some problem. This can either be a problem I worked on at work or a problem from a programming challenge website such as Leetcode.<br>Go through the recording at 10x speed and annotate what I was doing at each moment.<br>Total how much time I spent into high level categories. How much time did I spend debugging some bug? How much time did I spend building some feature<br>Look at the categories I spent the most time in. Then dig into what actually took up that time.<br>Come up with approaches that would have allowed me to save time. Often there are ways I could have structured my code up front that would have allowed me to write less code or find bugs earlier.<br>I highly recommend recording your screen. It’s one of the easiest ways to find small changes you can make to make yourself a lot more productive.</p><p>I’ve been doing this training regime over the past year. I’ve definitely noticed a huge difference. There’s a large amount of knowledge about systems and tools that I wouldn’t have developed otherwise. I’m also able to solve problems more quickly than I was able to before. I hope you reflect on these exercises and implement a few of them yourself.</p><p>Going forward, I’m going to start sharing what I find through my training regime. I’m going to start off by writing a blog post every time I do one of the exercises. I’ll share what I learned and found during the exercise. I think it would be beneficial for me to explain what I learned, as well as make a great learning resource for others.</p><!--    阅读卡：    文章：    时间：--><!--    阅读卡：    文章：    时间：    --><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="http://malisper.me/my-approach-to-getting-dramatically-better-as-a-programmer/?utm_source=wanqu.co&amp;utm_campaign=Wanqu+Daily&amp;utm_medium=website" target="_blank" rel="noopener">My Approach to Getting Dramatically Better as a Programmer</a><br>[My Approach to Getting Dramatically Better as a Programmer]:<a href="http://malisper.me/my-approach-to-getting-dramatically-better-as-a-programmer/?utm_source=wanqu.co&amp;utm_campaign=Wanqu+Daily&amp;utm_medium=website" target="_blank" rel="noopener">http://malisper.me/my-approach-to-getting-dramatically-better-as-a-programmer/?utm_source=wanqu.co&amp;utm_campaign=Wanqu+Daily&amp;utm_medium=website</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;
&lt;p&gt;There was a recent discussion amon
      
    
    </summary>
    
      <category term="English" scheme="http://yoursite.com/categories/English/"/>
    
    
      <category term="English" scheme="http://yoursite.com/tags/English/"/>
    
  </entry>
  
  <entry>
    <title>React Native</title>
    <link href="http://yoursite.com/React-Native/"/>
    <id>http://yoursite.com/React-Native/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-19T03:04:04.994Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://reactnative.cn/docs/0.36/getting-started.html" target="_blank" rel="noopener">React Native简介教程</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="React Native" scheme="http://yoursite.com/categories/React-Native/"/>
    
    
      <category term="React Native" scheme="http://yoursite.com/tags/React-Native/"/>
    
  </entry>
  
  <entry>
    <title>Linux</title>
    <link href="http://yoursite.com/Linux-Base/"/>
    <id>http://yoursite.com/Linux-Base/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-10T03:48:44.452Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Linux" scheme="http://yoursite.com/categories/Linux/"/>
    
      <category term="Base" scheme="http://yoursite.com/categories/Linux/Base/"/>
    
    
      <category term="Base" scheme="http://yoursite.com/tags/Base/"/>
    
      <category term="Linux" scheme="http://yoursite.com/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Python开发</title>
    <link href="http://yoursite.com/Python/"/>
    <id>http://yoursite.com/Python/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-07-30T07:16:38.275Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="https://juejin.im/entry/5a6b4ae3f265da3e3245b66f?utm_source=gold_browser_extension" target="_blank" rel="noopener">Python从入门到转行</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="Python" scheme="http://yoursite.com/categories/Python/"/>
    
    
      <category term="Python" scheme="http://yoursite.com/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>MAC OS</title>
    <link href="http://yoursite.com/Mac-OS/"/>
    <id>http://yoursite.com/Mac-OS/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-04T09:29:58.672Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.<br><!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><!--    阅读卡：    文章：    时间：--><p><a href="http://www.cnblogs.com/chijianqiang/archive/2011/08/03/2126593.html" target="_blank" rel="noopener">开始使用Mac OS X——写给Mac新人</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;br&gt;&lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;&lt;/p&gt;
&lt;h
      
    
    </summary>
    
      <category term="MAC OS" scheme="http://yoursite.com/categories/MAC-OS/"/>
    
    
      <category term="MAC OS" scheme="http://yoursite.com/tags/MAC-OS/"/>
    
  </entry>
  
  <entry>
    <title>Android</title>
    <link href="http://yoursite.com/Android-Base/"/>
    <id>http://yoursite.com/Android-Base/</id>
    <published>2015-12-31T16:00:00.000Z</published>
    <updated>2018-08-31T10:05:13.038Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Android-官网相关"><a href="#Android-官网相关" class="headerlink" title="Android 官网相关"></a>Android 官网相关</h1><h2 id="Google开发技术资源"><a href="#Google开发技术资源" class="headerlink" title="Google开发技术资源"></a>Google开发技术资源</h2><p><a href="https://chinagdg.org/resource-list/" target="_blank" rel="noopener">Google开发技术资源汇总</a></p><h2 id="Android源码开发指导"><a href="#Android源码开发指导" class="headerlink" title="Android源码开发指导"></a>Android源码开发指导</h2><p><a href="https://source.android.google.cn" target="_blank" rel="noopener">Android源码开发指导官网</a><br>包含Android的设置、安全性、移植、调整、兼容性、参考等几个模块.</p><p><a href="https://android-review.googlesource.com/" target="_blank" rel="noopener">Android源码</a><br>Android的源码网站，可以直接在线查看Android源码</p><h2 id="Android开发者网站"><a href="#Android开发者网站" class="headerlink" title="Android开发者网站"></a>Android开发者网站</h2><p><a href="https://developer.android.google.cn/" target="_blank" rel="noopener">Android开发者官网</a><br><a href="https://developer.android.google.cn/support" target="_blank" rel="noopener">Android开发者支持</a></p><p><a href="https://developer.android.google.cn/guide/" target="_blank" rel="noopener">Android技术简介-开发者指南</a><br><a href="https://developer.android.google.cn/guide/platform/" target="_blank" rel="noopener">Android平台技术</a><br><a href="https://developer.android.google.cn/reference/" target="_blank" rel="noopener">Android API</a><br><a href="https://developer.android.google.cn/samples/" target="_blank" rel="noopener">Android示例</a><br><a href="https://developer.android.google.cn/design/" target="_blank" rel="noopener">Android的材料设计</a><br><a href="https://developer.android.google.cn/jetpack/" target="_blank" rel="noopener">Android开发实用工具</a><br><a href="https://developer.android.google.cn/distribute/" target="_blank" rel="noopener">Android开发中Google Play商店</a>即Google的Android Market开发者网站<br><a href="https://developer.android.google.cn/preview/" target="_blank" rel="noopener">Android各版本功能预览</a><br><a href="https://developer.android.google.cn/studio/" target="_blank" rel="noopener">AndroidStudio官网</a><br><a href="https://developer.android.google.cn/studio/intro/" target="_blank" rel="noopener">AndroidStudio用户指南</a></p><p>Android App Bundles:Google最新推出的Apk动态打包，动态组件化的技术<br><a href="https://developer.android.google.cn/platform/technology/app-bundle/" target="_blank" rel="noopener">Android App Bundles</a><br><a href="https://developer.android.google.cn/guide/app-bundle/configure" target="_blank" rel="noopener">Android App Bundles</a></p><p><a href="https://developers.google.cn/programs/community/gdg/" target="_blank" rel="noopener">GDG Google开发者组</a></p><h2 id="GMS"><a href="#GMS" class="headerlink" title="GMS"></a>GMS</h2><p><a href="https://www.android.com/gms/" target="_blank" rel="noopener">GMS</a></p><h2 id="Google中国开发者"><a href="#Google中国开发者" class="headerlink" title="Google中国开发者"></a>Google中国开发者</h2><p><a href="https://developers.google.cn/china/" target="_blank" rel="noopener">Google中国开发者</a></p><h2 id="Android官方博客"><a href="#Android官方博客" class="headerlink" title="Android官方博客"></a>Android官方博客</h2><p><a href="https://blog.google/products/android" target="_blank" rel="noopener">Android官方博客</a> <a href="https://blog.google/products/android/rss/" target="_blank" rel="noopener">https://blog.google/products/android/rss/</a><br><a href="https://android-developers.googleblog.com/" target="_blank" rel="noopener">Android开发者博客</a><br><a href="https://security.googleblog.com/" target="_blank" rel="noopener">Google安全博客</a></p><p><a href="https://blog.csdn.net/haizhanmeng/article/details/70478981" target="_blank" rel="noopener">Android O 功能和 API</a></p><h1 id="查看Android源码的方式"><a href="#查看Android源码的方式" class="headerlink" title="查看Android源码的方式"></a>查看Android源码的方式</h1><p>1) Google-github<br><a href="https://github.com/android/" target="_blank" rel="noopener">https://github.com/android/</a><br><a href="https://github.com/android/platform_frameworks_base/" target="_blank" rel="noopener">https://github.com/android/platform_frameworks_base/</a></p><p>2).OpenGork<br><a href="http://androidxref.com/" target="_blank" rel="noopener">http://androidxref.com/</a><br><a href="https://www.androidos.net.cn/" target="_blank" rel="noopener">https://www.androidos.net.cn/</a><br><a href="http://android.macpod.net/" target="_blank" rel="noopener">http://android.macpod.net/</a></p><p>3).Google官网 (需要翻墙，不会翻墙的同学，还是用上面两种吧)<br><a href="https://android-review.googlesource.com" target="_blank" rel="noopener">https://android-review.googlesource.com</a></p><p>4）<a href="https://aosp-mirror.github.io/" target="_blank" rel="noopener">https://aosp-mirror.github.io/</a></p><p>★<a href="../Android开发者指南阅读">Android 开发者指南阅读</a><br>★<a href="../Android开发常用网站">Android开发常用网站</a></p><h1 id="Android工具网站："><a href="#Android工具网站：" class="headerlink" title="Android工具网站："></a>Android工具网站：</h1><p><a href="http://www.androiddevtools.cn/" target="_blank" rel="noopener">http://www.androiddevtools.cn/</a><br><a href="http://www.android-studio.org/" target="_blank" rel="noopener">http://www.android-studio.org/</a></p><h1 id="Android-开发技术周报"><a href="#Android-开发技术周报" class="headerlink" title="Android 开发技术周报"></a>Android 开发技术周报</h1><p><a href="https://androidweekly.io/" target="_blank" rel="noopener">https://androidweekly.io/</a>  每周一篇最新的技术周报<br><a href="https://androidweekly.cn/" target="_blank" rel="noopener">https://androidweekly.cn/</a><br>Android 开发的技术文章、开源项目、开发工具、视频教程、Android 设计教程、设计资源等</p><!--# Android技术专家罗升阳[老罗的CSDN博客](https://blog.csdn.net/luoshengyang)[那两年炼就的Android内功修养](https://blog.csdn.net/Luoshengyang/article/details/8923485) 这篇文章是老罗对自己Android文章的汇总简介：Android系统源代码情景分析，一书作者，浙大计算机系本科，上海交大硕士郭霖微信公众号:郭霖[郭霖CSDN博客](https://blog.csdn.net/guolin_blog/)简介：圈内人称郭神，Android最好的入门书籍《第一行代码》的作者。博文行文流畅，条理清晰，内容实用，适合读完入门书籍之后阅读，超高人气博主。任玉刚[任玉刚CSDN博客](https://blog.csdn.net/singwhatiwanna/)简介：中科大硕士，百度Android资深工程师，进阶书籍《Android开发艺术探索》作者，博客极具深度广度，适合Android开发者的高阶学习。最近任玉刚老师推出了与书籍相配套的视频教程，感兴趣的同学可以关注下。张鸿洋[张鸿洋CSDN博客](https://blog.csdn.net/lmj623565791/)兰亭风雨[兰亭风雨CSDN博客](https://blog.csdn.net/ns_code)简介：圈内公认的Java大牛，对Java虚拟机有深度研究，出色的Java源码分析Mr.Simple[Mr.Simple CSDN博客](https://blog.csdn.net/bboyfeiyu)简介：开源框架专家，OOP等，《从小工到专家》、《Android设计模式》作者。博主手中就有一本从小工到专家，性能优化部分非常不错，热爱分享的技术大牛，值得推荐。爱哥[爱哥CSDN博客](https://blog.csdn.net/aigestudio/)简介：博主偶然间搜索到他自定义控件部分的博客，深入浅出，幽默诙谐，逼格满满，自定义View专家。胡凯[胡凯个人博客](http://hukai.me/)简介：精通Android性能优化，内存优化，长期坚持翻译国外先驱技术博客。张明云[张明云github博客](http://zmywly8866.github.io/)[张明云的github](https://github.com/zmywly8866)[张明云的CSDN博客](https://blog.csdn.net/ekeuy)张明云老师长期活跃于知乎、简书等技术平台，对性能优化、各种坑的解决颇有研究并且乐于分享，简书上也有张明云老师的专栏，强烈推荐！stormzhang[stormzhang博客精华](http://stormzhang.com/)张涛[开源实验室](https://kymjs.com/)简介：专注于Kotlin段启智[Duanqz的博客](http://duanqz.github.io)[Duanqz的github](https://github.com/duanqz)duanqz@gmail.comhttp://weibo.com/duanqz简介：专注Android源码分析邓凡平[深入理解 Android 卷II](http://wiki.jikexueyuan.com/project/deep-android-v2/)[邓凡平CSDN博客](http://blog.csdn.net/innost)fanping.deng@gmail.com简介：深入理解 Android 卷II 一书作者.段建华[技术小黑屋](https://droidyue.com)[干货铺子](https://droidyue.com/ninki/)HenCoder：http://hencoder.comhttp://plus.hencoder.com  有部分MVC/MVP的知识https://gank.io/post/560e15be2dca930e00da1083HenCoder Android 开发进阶：自定义 View: http://hencoder.com/ui-1-3/Gityuanhttp://gityuan.com微信公众号： Android达摩院Android系统工程师，曾就职于IBM、Lenovo，目前就职于小米MIUI系统xu的博客：https://xudeveloper.github.io写了几篇源码解析，行文思路值得借鉴GcsSloop[View的知识](https://github.com/GcsSloop/AndroidNote)[Android学习笔记](http://www.gcssloop.com/#blog)简介：对View的基础知识讲解的比较透彻凶残的程序员https://blog.csdn.net/qian520ao 专长：Java基础、Window和ViewHohohonghttps://www.jianshu.com/u/107bd58d5bbb 专长：讲Window和View、EventBus、OkHttphttps://my.csdn.net/huangyabin001 专长：SeLinuxhttp://codemx.cn/2016/12/18/Launcher07/ 有Launcher模块的文章值得一看[Android知识梳理](http://codemx.cn/2016/05/04/2016-05-04-Android-Tree/)★★★★★-----------参考----------[大神博客](https://github.com/ColorfulCat/AndroidGuide#5-%E5%A4%A7%E7%A5%9E%E5%8D%9A%E5%AE%A2)[所有Android开发者必须阅读的博客整理](https://blog.csdn.net/melodev/article/details/51235427)[Android开发大牛部分汇总](https://github.com/android-cn/android-dev-cn)# 值得关注的Android公众号:微信公众号：Android面试启示录微信公众号：Android达摩院汤涛：微信公众号：androidtrening如果你是一个中高级Android开发者，那汤涛哥的公众号你有必要关注一下了。汤涛哥的公众号每一篇都是精品，宁缺毋滥，专注于Android开发实战，汤涛哥人也非常好，热爱分享，专注技术深度挖掘。移动开发前线：微信公众号：bornmobile分享优质iOS、Android、前端技术精品文章--><h1 id="Android-代码统计"><a href="#Android-代码统计" class="headerlink" title="Android 代码统计"></a>Android 代码统计</h1><p>以Android8.1.0的一套代码为例,大概的各种语言代码行数如下，我们可以看到，前几种语言分别是C/C++/Java/XML/Python<br>整个Android代码涉及到的语言也蛮多的，几乎主流的开发语言都包括进来了.<br>整套Android代码，大概有34万左右的文件,纯代码大概有8000万行左右.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">Language                      files          blank        comment           code</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">C                             54148        4245419        4686600       23598682</span><br><span class="line">C/C++ Header                  69040        2184827        5014483       19738711</span><br><span class="line">C++                           39853        1839583        1850860       11092399</span><br><span class="line">Java                          70997        2090664        4075908       10939064</span><br><span class="line">XML                           51780         221344        1283239        9081732</span><br><span class="line">Assembly                      12460         242489         331413        1961720</span><br><span class="line">Python                        10238         417686         558163        1631669</span><br><span class="line">HTML                           3597         185219          26669        1276487</span><br><span class="line">Bourne Shell                   4385         195824         221183        1170153</span><br><span class="line">D                             10327          37070              0        1090896</span><br><span class="line">Expect                         1317          31376          17854         524894</span><br><span class="line">m4                              711          31173          10159         292521</span><br><span class="line">Perl                            601          33637          30096         213867</span><br><span class="line">Objective C                    1583          31542          86970         139743</span><br><span class="line">make                           4146          29879          33894         128933</span><br><span class="line">Javascript                      381          14469          24684         111975</span><br><span class="line">C#                              559          12811          26542          84491</span><br><span class="line">Teamcenter def                  305           5508           2962          53315</span><br><span class="line">CSS                             175           7818           2639          47710</span><br><span class="line">Fortran 77                       63             77          19999          42841</span><br><span class="line">Bourne Again Shell              840           8245          12232          39987</span><br><span class="line">yacc                             47           5243           2816          37264</span><br><span class="line">IDL                              82           5150              0          30521</span><br><span class="line">PHP                             108          10265           1451          29640</span><br><span class="line">lex                             456           2090           1897          29251</span><br><span class="line">Ruby                             93           5147           2058          23109</span><br><span class="line">Lisp                            259           3062            926          20540</span><br><span class="line">XSLT                             83           1804           1544          17841</span><br><span class="line">Groovy                          273           4627          10028          17262</span><br><span class="line">Pascal                           15           2146            753          14724</span><br><span class="line">YAML                            525           1015           2539          14534</span><br><span class="line">XSD                             210           2160           3797          14457</span><br><span class="line">DOS Batch                       182           1894           1386           8694</span><br><span class="line">DTD                              43           1540           3214           5162</span><br><span class="line">Lua                              36            662            409           4502</span><br><span class="line">awk                              43            435           1056           4262</span><br><span class="line">ActionScript                     56            860           2550           3715</span><br><span class="line">ASP.Net                          65            312             67           3504</span><br><span class="line">MSBuild scripts                  20              1            118           2212</span><br><span class="line">SQL                              11            161            431           2096</span><br><span class="line">MATLAB                           42            391            305           1703</span><br><span class="line">Ada                              10            599            560           1681</span><br><span class="line">NAnt scripts                     10            179             36            835</span><br><span class="line">vim script                       14            133            152            661</span><br><span class="line">sed                              30             57            198            659</span><br><span class="line">Visual Basic                     16             32             61            503</span><br><span class="line">Korn Shell                        3             71             70            376</span><br><span class="line">C Shell                           4             27             28            240</span><br><span class="line">Haskell                           2             84             58            195</span><br><span class="line">MUMPS                             4             19              1            177</span><br><span class="line">JSP                               2              3              0             51</span><br><span class="line">Fortran 95                        1              3              0             18</span><br><span class="line">Tcl/Tk                            1              6              6             11</span><br><span class="line">Fortran 90                        3              1            263              8</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">SUM:                         340255       11916839       18355327       83552198</span><br><span class="line">--------------------------------------------------------------------------------</span><br></pre></td></tr></table></figure><h1 id="Android书单"><a href="#Android书单" class="headerlink" title="Android书单"></a>Android书单</h1><p><a href="https://www.jianshu.com/p/9a80e48ef7bc" target="_blank" rel="noopener">移动开发必读书单</a></p><!--    阅读卡：    文章：    时间：-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Android-官网相关&quot;&gt;&lt;a href=&quot;#Android-官网相关&quot; class=&quot;headerlink&quot; title=&quot;Android 官网相关&quot;&gt;&lt;/a&gt;Android 官网相关&lt;/h1&gt;&lt;h2 id=&quot;Google开发技术资源&quot;&gt;&lt;a href=&quot;#G
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Base" scheme="http://yoursite.com/categories/Android/Base/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Base" scheme="http://yoursite.com/tags/Base/"/>
    
  </entry>
  
  <entry>
    <title>关于工作和成长的121条具体建议</title>
    <link href="http://yoursite.com/Life-Advice/"/>
    <id>http://yoursite.com/Life-Advice/</id>
    <published>2015-09-08T16:00:00.000Z</published>
    <updated>2018-07-25T02:24:14.661Z</updated>
    
    <content type="html"><![CDATA[<!--    Module:    第一步:需要将这些文章都看几篇    第二步：形成自己的理解    第三步：可以按照非常有用的文章，重写写一遍    第四步：纠正或者延伸别人的文章--><h1 id="关于面对批评"><a href="#关于面对批评" class="headerlink" title="关于面对批评"></a>关于面对批评</h1><p>01 没有人对被批评感到高兴。如果有，TA撒谎。</p><p>02 面对批评，得体的第一反应是“不急于解释，不反唇相讥”。</p><p>03 每天，或者最长每周养成习惯，把自己存在的问题和造成的麻烦用最重的形容词想一遍，诚实的对待自己。当被批评时，会觉得一切还在掌握之中。</p><p>04 很多批评的发生是因为误会。所以，首先建立情感层面的信任，其次澄清事实，然后才是消除误会。这三步能解决大部分因批评带来的关系问题。</p><p>05 被批评时，思考的重点应该是“我应该从哪里入手解决问题”，而不是“对不对”。</p><p>06 对“评论”不理不睬，对“批评”高度重视。</p><p>07 如果对批评有不同意见或者不明之处，直接问当事人的完整意见。别回避。</p><h1 id="关于单身生活"><a href="#关于单身生活" class="headerlink" title="关于单身生活"></a>关于单身生活</h1><p>08 单身过的不愉快，有伴侣之后也不会愉快。</p><p>09 单身且愉快，一个重要前提是有较高收入且未来可期。</p><p>10 单身时最值得花时间去做的是投资一切长本事长能耐的事儿：学习、进修、放纵好奇心，因为在有伴侣以后很难再有大块时间可以自由支配。</p><p>11 拥有联系非常紧密的至交好友，且能够共同成长，两个条件缺一不可。不够紧密则对彼此生活缺少实际支持，不能同步成长则无法长期维持关系。</p><p>12 建立自己的生活秩序，但要有弹性。弹性是指随时可以接纳一个因伴侣而带来的新秩序。很多人被动长期单身的原因是因为沉浸在自己的世界里、过度缺乏弹性以至于无法接纳别人。</p><p>13 积极参加有意思的社交活动，认识陌生人，多交朋友。</p><p>14 尽快建立一套自己的“生活资源系统”：从保姆小时工到相熟的专车司机、发型师、健身教练、全科医生、可24小时喊出来帮忙的亲友、直到旅行代理人，把自己的生活置入一种由专业人士服务的环境中。</p><p>15 无论以戏谑或者认真的方式哭诉单身狗的身份，都应该尽量减少。因为最有吸引力的单身是貌似不知道是否真的单身的单身男女。</p><p>16 即使非常享受单身生活，也要保持“可勾搭”的待机状态，因为促进荷尔蒙分泌有益身心健康。</p><p>17 选择偏保守的理财方式，给自己买足额的大病保险，小心对待自己的钱。</p><p>18 和婚姻生活质量较高的人交朋友，人在面临重大选择时需要榜样和示范。</p><p>19 不要上来就用长期关系的标准来要求对方，关系是递进的：可见、可约、可睡、可长期交往，然后才是可固定长期关系。在不同阶段，标准不一样。</p><p>20 多参加打球、逛街、饭局、旅行，少参加茶会、禅修、面壁，不要给自己机会沉浸在自怨自怜伤春悲秋的情绪里。</p><p>21 主动点。对一切事。</p><h1 id="关于个人形象管理"><a href="#关于个人形象管理" class="headerlink" title="关于个人形象管理"></a>关于个人形象管理</h1><p>22 对普通人来说，“好看”的意思其实等同于“体面”。做到体面，完全是任何人能力范围内的事情。并非苛求。</p><p>23 认真看两部《Suits》这样的职场美剧，就知道形象管理的红线在哪里。</p><p>24 清洁是最低标准。对于在现代化城市工作生活的职场人来说，清洁的定义就是每天洗澡每天换衣服擦皮鞋。</p><p>25 你看起来像什么地位，你就是什么地位。</p><p>26 请看起来形象最好的朋友推荐发型师，然后和发型师成为朋友。考察一个好发型师的标准之一是他会给你相对而言最简单的方案而不是最贵的。</p><p>27 把衣服的数量最小化，然后把单价提高到力所能及范围内的最高。</p><p>28 尽快找出自己的“基本形象”：任何人都会有一个最佳穿衣模式，固定下来。范冰冰也得把胳膊大腿挡起来的时候才最好看。只是大多数人因为不够自信而不肯把问题简单化，总是在做无谓的尝试。</p><p>29 在职场露乳沟、露腋毛、露半拉屁股，都跟美丑无关，只是不体面。</p><p>30 健身可以帮助你更好的认知自己的身体，再贵的衣服也纠正不了驼背弯腰的仪态。</p><p>31 总体而言修身（不是紧身）的造型对男女来说都会显得人更精神。</p><p>32 不存在减不掉的体重，只是有没有纪律的问题。</p><p>33 一个人从里到外让人有“看起来好干净”的感觉，是一种极高的形象水平。这一条对老头儿都适用。</p><p>34 那些质疑个人形象管理重要性的人从来都不会知道因为糟糕的形象而损失了什么。</p><h1 id="关于重大选择"><a href="#关于重大选择" class="headerlink" title="关于重大选择"></a>关于重大选择</h1><p>面临事业、婚姻等等改变人生轨迹的重大选择时，我的心法，仅供参考。</p><p>35 尽自身最大努力做到有钱、好看、有本事、受欢迎，手里的牌多一些，做选择的主动性就高一些。</p><p>36 选择大于能力。有人管这个就叫“命运”。其实是一种长期被忽略的能力：关键时刻快速做出最优选择的能力。</p><p>37 最优选择就是对个人远期价值最大化的选择。过于关注当下利益往往是人生的大坑。那些抱着要给未来的孩子找个好爸爸的女生在婚姻生活中往往过得比寻找最佳男朋友的女生要好，就是这个原理。</p><p>38 成大事者不纠结。一旦决定，全力以赴。</p><p>39 掌握“概率权”。两张牌，一张掀起来保证给你一百万美金，另一张有百分之五十的概率有1个亿美金，或者为零。想清楚，选哪张？怎么选？</p><p>40 可以征求别人的意见。但是这个“别人”是特指那些你由衷佩服和学习的对象。</p><p>41 无论别人给你什么意见，都要记住决定是你自己的。不要依赖别人，更不要把后果归罪于外。</p><p>42 远离安全的舒适区。</p><p>43 远离颠倒梦想，乐于动手做具体事，少想，多干。</p><p>44 做选择时不要只看自己，要看如果做了一个决定之后，你会和什么样的人混在一起。事业选择这一点尤为重要。</p><h1 id="关于人际关系"><a href="#关于人际关系" class="headerlink" title="关于人际关系"></a>关于人际关系</h1><p>45 良性的人际关系只有一种，叫做独立自主、强强联合。从来就没有抱团取暖这回事。</p><p>46 做到对别人有价值，是建立良性人际关系的前提，哪怕是暂时只创造了微小的价值，也有价值。</p><p>47 所有关系中最多正向循环最少事后负担的，是交易关系。市场最残酷但也最善意。</p><p>48 所有关系中最危险最有破坏力的，是纯感情关系。所以，一段关系要想良性发展，要有能力从纯感情喂养，发展成“复合材料”：比如就婚姻而言，激情不长久，但是双方可以成为共同成长的伙伴，或者某个具体目标的合作者。甚至就亲子关系而言，也可以发展出协同学习的伙伴关系。</p><p>49 不成为别人的负担。这包括了不成为别人的心理和时间负担，接受别人对自己的不接受。</p><p>50 对自己负责。这包括了对自己的选择和决定负责，承受由之而来的任何结果。</p><p>51 定期梳理和剖析自己的原生家庭和亲密关系。适度学习一点帮助自我认知的方法，清楚认识并且正面接受自己在人际关系中的短板和问题。大量的人际关系能力缺陷是在原生家庭中已经形成，无需自卑或者自责，因为正面接受自己是战胜这一问题的开始。而且大部分人的大部分心理障碍是可以被消除的。</p><p>52 第一反应是选择信任别人，但是同时保持独立思考的能力。</p><p>53 人际关系不仅有交互频率这一个维度，还有交互深度。熟人未必是知己。</p><p>54 远离巨婴、远离不具有建设性能力的人。</p><p>55 人际关系是一个人真实自我的外在镜像。</p><h1 id="关于命中贵人"><a href="#关于命中贵人" class="headerlink" title="关于命中贵人"></a>关于命中贵人</h1><p>56 贵人是那些在关键时刻给出关键点拨的人。他们有能力呈现世界的本来面目。路原来就在那里，但是没有他们的指路你就看不见。</p><p>57 他们之所以愿意帮你是因为他们的修行，而不是因为我的能耐和好处。</p><p>58 对他们最好的回报是努力成为和他们一样的人。</p><p>59 贵人不是等到的，是寻到的和求到的。前提是对他们而言至少你不是减分项。</p><p>60 以求道之心与人交往。</p><p>61 举手之劳、锦上添花，才是良性关系。不要求人雪中送炭。跟世态炎凉没关系，有则感谢珍惜，无则检讨自己。</p><p>62 务必定期让人看到帮你的结果，分享喜悦和成果。</p><p>63 逢年过节快递两盒点心，送一辈子，也不算有礼。若想要感谢对方，花心思观察，送出终身难忘的礼物。</p><p>64 不要黏着对方，不要成为别人的负担。要让对方感觉掌握关系的主动权。人家帮过你，但最怕被要求帮你一辈子。</p><p>65 有机会帮别人的时候，姿态放低再放低，尽量不让对方有心理压力。帮完之后，对方不提，自己不提。</p><p>66 也许终有一天你会超越那些你生命中曾经那么重要的人，往前看，别害怕。既往不恋。</p><h1 id="关于压力管理"><a href="#关于压力管理" class="headerlink" title="关于压力管理"></a>关于压力管理</h1><p>67 压力是公平的。真正在入世过活的人，没有人能够置身事外。坚信这一点，不易起怨懑之心。</p><p>68 职场压力和生活压力无法互换消解。因此不要把压力释放错了地方。</p><p>69 做一个乐观的悲观主义者。因上努力，果上随缘。</p><p>70 充分想象最坏的结果，如果认为是无法承受的后果，一定源于自身的贪婪，果断踩刹车。君子不立危墙之下。</p><p>71 压力无法被替代，注意力可以被转移。到难以承受之际，刷一小时消消乐，剧烈运动两小时，都可以回血。问题依然还在。但不妨缓口气再来。</p><p>72 有无话不说且旗鼓相当的朋友。说出来可能是最有效的减压之道，并非有人可以安慰你，而是因为站在别人的视角重新看一遍问题会简单很多。</p><p>73 做不到72，就试着把压力和问题写下来，只给自己看。然后会发现其实没有那么复杂。</p><p>74 永远用最直接的方式面对压力源。拖延和迂回只会让压力变大。</p><p>75 如果压力影响睡眠超过一星期，立即服用褪黑素。否则一个月后大概率就是抑郁症。</p><p>76 永远不做任何不能让别人知道的决定和交易，无论好处有多大。</p><p>77 不要用血拼、喝酒、嗑药、饕餮、滥交来缓解压力，否则一觉醒来你会面对压力2.0。</p><p>78 把压力想象成一个具体的形象，比如一只怪兽，每处理一步，就在脑海里给丫一拳。</p><h1 id="关于工作习惯"><a href="#关于工作习惯" class="headerlink" title="关于工作习惯"></a>关于工作习惯</h1><p>79 自己最受益的工作习惯是做笔记。最佩服的是会面后最先共享笔记的人。</p><p>80 当日事当日毕能提升工作中的幸福感。</p><p>81 有关人的问题，都不可拖延，不要心存侥幸，认为可以避免直面冲突或对方可以心领神会。越晚着手，问题会恶化的越严重。难听话必须当面说、尽早说、直接说。</p><p>82 个人行动养成彩排的习惯。打腹稿、做预案。认真准备，就能发现达到一个目标的N种途径，行动中灵活不纠结。</p><p>83 团队作业养成复盘的习惯。不追究具体人责任，着眼于我们学到了什么。</p><p>84 如果工作性质与人打交道比较多，尽量把会议、会面集中安排在一周中的某几天，这样总时间会缩短。可以留下自己书面作业和从事内部事务的时间。</p><p>85 如果是管理者，每天固定拨出一些时间与团队成员做非事务性交谈。少则扯淡十几分钟亦可。养成尊重和关注人的习惯。</p><p>86 每天琢磨核心数据。问出好问题。</p><p>87 与人面对面交流时不刷手机。</p><p>88 开会时敢于并善于终结无实质意义的对话，直切主题。致力于达成行动共识。</p><p>89 准确记录与别人的约会，不迟到。</p><p>90 做粗略的工作计划比做详细的计划更接近实际情况，更容易被执行。</p><p>91 出席别人的活动，认真做事前准备，不可一套说辞走世界。准备全套的个人公开资料及时提供给主办方。尽早提交讲稿。</p><p>92 每天早中晚三次集中时间段处理社交媒体、邮件等方面的信息。不要随刷随到。这是最节省时间的办法。</p><p>93 如果是管理者，学会问和听一个好问题：你有什么想法？</p><p>94 购置最好的工作装备。</p><p>95 睁大双眼找个好搭档。和聪明人一起工作。</p><h1 id="关于自我成长"><a href="#关于自我成长" class="headerlink" title="关于自我成长"></a>关于自我成长</h1><p>96 早晚你会知道，这个世界上没有别人。你所看到的都是你自己的认知模式创造的镜像。</p><p>97 与此同时你会知道，这个世界上全是别人。所谓反复追寻的“真我”实在是小到不能再小的东西。</p><p>98 同时知道并接受上述两点，自我成长这件事才真正开始。</p><p>99 最难的在于不断建立更高的标准，为了解决这一问题最有效的方式是不断结识更好的榜样。</p><p>100 在人际关系中做一个能力型“势利眼”，向上看、向前看，与比自己优秀的人交往。</p><p>101 既往不恋。不回头。不怀旧。不惋惜。</p><p>102 以真实的、认识的人为榜样，而不是“传说”中的。因为真人会在每天的交往中给你真实的压力。</p><p>103 要寻找和接收那种叫做“Calling”的东西。克服“如何怎样就很好了”的小地主心态。</p><p>104 慎独。守心如镜。</p><p>105 每天独处时问问自己，今天有什么是比前一天做的好的。哪怕是对快递更认真的说谢谢。</p><p>106 不做负面表达，负面包括讽刺、抱怨、指责、争论、批评、牢骚、大话、评价议论。凡事从建设性出发。</p><p>107 认真记录并揣摩与别人的交往。</p><p>108 尽可能扩大自己的阅读面。文科生多读点科学著作，理科生不妨研究点文学艺术。阅读是为了理解人。</p><p>109 带徒弟是逼迫个人成长的绝招。</p><p>110 不相信“适当的年龄”这件事。除了生孩子，想做任何事情都不会被年龄限制。</p><h1 id="关于机会与陷阱"><a href="#关于机会与陷阱" class="headerlink" title="关于机会与陷阱"></a>关于机会与陷阱</h1><p>111 世界上最大的陷阱，叫做“机会型陷阱”。</p><p>112 评估一下这件事对多少人有利？越是多赢的局面，越可能是个机会，反之如果只有你占尽便宜，那肯定不是机会。</p><p>113 想想这件事是不是很容易做到？有两种事。一种是机灵事：开头就炸，但是越来越没劲。另一种是苦逼事：开头特难，但是越干门槛越高。前者是陷阱，后者是机会。结硬寨、打呆仗是不变的真理，很容易实现的事情，是机巧，不是机会。</p><p>114 看忽悠你干这个事的人跟这事的关系。如果对他在精力上只是业余兼职，在财富上只是锦上添花，而你要付出全部精力，搭上全部身家。那么，要警惕是个陷阱。</p><p>115 周边人赞成与否不重要，但是否出手帮你很重要。一旦开始干，帮你的人越来越多的事，机会。反之，陷阱。</p><p>116 因资源而启动的事，容易翻转为陷阱。因顺势而发生的事，容易找到机会。</p><p>117 推动社会新分工的事，也就是越来越多的人靠这事吃饭养活一家老小的事，机会。在各种裂缝中套利，除了你谁都没好处的事，陷阱。</p><p>118 做成之后，容易被巨无霸们摘果子的事，陷阱。做成之后，自成体系的事，机会。换句话说，南瓜不会结在树上。有命长出来，没命hold住。</p><p>119 就算做成了，也有后遗症的事，陷阱。就算做不成，也长本事长江湖地位的事，机会。</p><p>120 摊在桌上打明牌也能干的事儿，机会。必须遮遮掩掩唯恐别人知道的事儿，陷阱。</p><p>121 你孩子长大了为你骄傲的事，机会。反之，陷阱。</p><p>以上全都能做好的，不是人，是真神，脱神也未必都能做好。我们在心里列好这么一个清单，去要求自己，改变自己，完善自己。任何一点改变都需要时间，我们生来不完美，所以要用一生的时间缝缝补补。</p><!--    阅读卡：    文章：    时间：2018-07-25--><!--# 参考文献[罗辑思维CEO脱不花：关于工作和成长，这是我的121条具体建议](https://mp.weixin.qq.com/s?__biz=MzI4NDMxMjczNg==&mid=2247487758&idx=1&sn=f880dd957b42f769773b7ee57d17afb1&chksm=ebfc04c1dc8b8dd7096b3e53d9b01eb7f80649b012839a23a4c0d9753bf30fa9c1db95dbd127&mpshare=1&scene=21#wechat_redirect)[罗辑思维CEO脱不花：关于工作和成长，这是我的121条具体建议]:https://mp.weixin.qq.com/s?__biz=MzI4NDMxMjczNg==&mid=2247487758&idx=1&sn=f880dd957b42f769773b7ee57d17afb1&chksm=ebfc04c1dc8b8dd7096b3e53d9b01eb7f80649b012839a23a4c0d9753bf30fa9c1db95dbd127&mpshare=1&scene=21#wechat_redirect-->]]></content>
    
    <summary type="html">
    
      
      
        &lt;!--
    Module:
    第一步:需要将这些文章都看几篇
    第二步：形成自己的理解
    第三步：可以按照非常有用的文章，重写写一遍
    第四步：纠正或者延伸别人的文章
--&gt;
&lt;h1 id=&quot;关于面对批评&quot;&gt;&lt;a href=&quot;#关于面对批评&quot; cla
      
    
    </summary>
    
      <category term="Life" scheme="http://yoursite.com/categories/Life/"/>
    
      <category term="Advice" scheme="http://yoursite.com/categories/Life/Advice/"/>
    
    
      <category term="Advice" scheme="http://yoursite.com/tags/Advice/"/>
    
      <category term="Life" scheme="http://yoursite.com/tags/Life/"/>
    
  </entry>
  
  <entry>
    <title>适配器模式</title>
    <link href="http://yoursite.com/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F/"/>
    <id>http://yoursite.com/适配器模式/</id>
    <published>2015-08-01T09:34:49.000Z</published>
    <updated>2018-06-23T15:24:29.019Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://blog.csdn.net/beyond0525/article/details/22814129" target="_blank" rel="noopener">http://blog.csdn.net/beyond0525/article/details/22814129</a></p><p>适配者模式是也是android中应用很广泛的设计模式，如我们常见用 BaseAdpter, ArrayAdapter, CursorAdapter,就是用的适配者模式，看到源码你是不是对这种设计方式很陌生，没事，下面我们通过实际的例子来取理解这种设计模式。</p><p>一、作用</p><p>适配器模式（Adapter）：将一个类的接口转换成客户希望的另外一个接口，使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。<br>二、适用场景</p><ol><li><p>业务的接口与工作的类不兼容，（比如：类中缺少实现接口的某些方法）但又需要两者一起工作</p></li><li><p>在现有接口和类的基础上为新的业务需求提供接口</p></li></ol><p>三、常见的使用方式</p><p>还是以Usb接口和Phone手机类的产品举例子，假设设计的Phone类中有 call(), sms(), takeAlong()属性方法，而在设计Usb接口时定义了 store(), takeAlong()的行为。如果现在有新的业务需求，需要生成 Xiaomi手机类具有 Phone类和Usb接口两者功能，假设Phone类和Usb接口已经在业务上投入使用，很显然，去修改原类中的方法和接口的行为去满足现在的新业务需求是不可取的，那么现在适配者模式就派上用场了。</p><p>（1）类适配模式</p><p>大致的意思是新的业务类Xiaomi通过继承旧业务的类Phone并实现接口Usb来满足新的业务的一种适配方式，如下图</p><p>Usb接口</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>public interface Usb {</p><pre><code>void store();  void takeAlong();  </code></pre><p>}<br>Phone类<br>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>public class Phone {</p><pre><code>public void call() {      System.out.println(&quot;Phone call&quot;);  }  public void sms() {      System.out.println(&quot;Phone sms&quot;);  }  public void takeAlong() {      System.out.println(&quot;Phone takeAlong&quot;);  }  </code></pre><p>}<br>适配 Xiaomi 类<br>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>/*<em><br>\</em> 类的适配器模式<br>* phone + Usb<br>* 将Phone的功能扩展到Usb里<br>* @author xuzhaohu  </p><ul><li><p>*/<br>public class Xiaomi extends Phone implements Usb {</p><p> @Override<br> public void store() {  </p><pre><code>// TODO Auto-generated method stub  System.out.println(&quot;store implements usb&quot;);  </code></pre><p> }  </p></li></ul><p>}<br>适配完后使用</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>Xiaomi mi1 = new Xiaomi();<br>mi1.takeAlong();<br>mi1.store();<br>输出：<br>Phone takeAlong<br>store implements usb</p><p>这样新的业务需求就可以通过适配的 Xiaomi类去满足了。是不是觉得很简单呢！！:) 有没有其他的方式去实现同样的功能呢？当然有，就是下面要讲的对象适配模式。<br>（2）对象适配模式</p><p>实现的方式很简单，其实就是在适配的时候通过构造函数将旧的业务Phone 当作新的适配类（XiaomiWrapper）一个成员对象去处理，然后适配类只需要实现接口 Usb即可。如下类关系图</p><p>适配类XiaomiWrapper如下，注意takeAlong()方法，是直接调用原类对象（Phone）去执行的。</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>/*<em><br>\</em> 对象的适配器模式  </p><ul><li>* @author xuzhaohu  </li><li><p>*/<br>public class XiaomiWrapper implements Usb {</p><p> /** </p><ul><li>1.创建一个Wrapper类，持有原类的一个实例， </li><li><p>2.在Wrapper类的方法中，调用实例的方法就行<br>*/<br>private Phone phone;  </p><p>public XiaomiWrapper(Phone phone) {  </p><p> this.phone = phone;<br>}  </p><p>@Override<br>public void store() {<br> // TODO Auto-generated method stub<br> System.out.println(“store implements usb”);  </p><p>}  </p><p>@Override<br>public void takeAlong() {<br> // TODO Auto-generated method stub<br> phone.takeAlong();<br>}  </p></li></ul></li></ul><p>}<br>适配完后通过构造函数将原对象传入即可。<br>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>XiaomiWrapper mi2 = new XiaomiWrapper(new Phone());<br>mi2.takeAlong();<br>mi2.store();<br>输出：<br>Phone takeAlong<br>store implements usb</p><p>或许到这里，你会觉得这种方式很简单吧。但是如果出现这个Usb接口中有很多方法（大于2个），但是新的业务需求中也只需要其中的一两个，而且是需要适配很多这样的业务，这样的话，用上面的方法每次适配一次就会去实现所有Usb接口中的方法，实际上适配的类中有很多是用不到的，没有必要把接口中不使用的类也适配进去，这时候，就轮到下面的接口适配模式出场了。</p><p>（3）接口适配模式</p><p>适配新的业务需求的时候借助抽象实现类（AbsPhone实现Usb接口），也就说，抽象实现类把Usb接口中的行为都实现了，新的适配是需要跟抽象类对话就行，因为抽象实现类就能满足了所有适配的需求，并且做到了只适配业务本身的行为，接口中不需要的行为我根本不需要关注。这就是抽象实现类的作用。类图关系如下：</p><p>抽象类AbsPhone实现</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>/*<em><br>\</em> 接口的适配器模式<br>* 1.借助于一个抽象类，该抽象类实现了该接口，实现了所有的方法<br>* 2.继承类可以选择性的实现接口中的方法  </p><ul><li>* @author xuzhaohu  </li><li><p>*/<br>public abstract class AbsPhone implements Usb {</p><p> public void store() {  </p><pre><code>System.out.println(&quot;AbsPhone implements usb&apos;s store methond&quot;);  </code></pre><p> }  </p><p> public void takeAlong() {  </p><pre><code>System.out.println(&quot;AbsPhone implements usb&apos;s takeAlong methond&quot;);  </code></pre><p> }  </p></li></ul><p>}<br>适配类只跟AbsPhone打交道，根本不需要关心接口的行为，只显示自己所要关注的。<br>如Phone1适配只需要store()行为</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>public class Phone1 extends AbsPhone {</p><pre><code>public void call() {      System.out.println(&quot;Phone1 call&quot;);  }  public void sms() {      System.out.println(&quot;Phone1 sms&quot;);  }  public void store() {      System.out.println(&quot;Phone1 need usb&apos;s store methond&quot;);  }  </code></pre><p>}<br>Phone2适配只需要takeAlong()行为<br>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>public class Phone2 extends AbsPhone {</p><pre><code>public void call() {      System.out.println(&quot;Phone2 call&quot;);  }  public void sms() {      System.out.println(&quot;Phone2 sms&quot;);  }  public void takeAlong() {      System.out.println(&quot;Phone2 need usb&apos;s takeAlong methond&quot;);  }  </code></pre><p>}<br>实例化调用<br>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>Phone1 p1 = new Phone1();<br>Phone2 p2 = new Phone2();<br>p1.store();<br>p2.takeAlong();<br>输出：<br>Phone1 need usb’s store methond<br>Phone2 need usb’s takeAlong methond<br>来一次完整的调用</p><p>[java] view plaincopy在CODE上查看代码片派生到我的代码片<br>Phone1 p1 = new Phone1();<br>Phone2 p2 = new Phone2();<br>p1.store();<br>p1.takeAlong();<br>p2.takeAlong();<br>p2.store();<br>输出：<br>Phone1 need usb’s store methond<br>AbsPhone implements usb’s takeAlong methond<br>Phone2 need usb’s takeAlong methond<br>AbsPhone implements usb’s store methond<br>这样很清晰的知道适配的什么方法了。</p><p>总结：适配者模式在android源码中有很多这样的体现，大家看完这个再去看源码，是不是会感觉看懂了不少呢？</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;http://blog.csdn.net/beyond0525/article/details/22814129&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://blog.csdn.net/beyond0525/article
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
  </entry>
  
  <entry>
    <title>观察者模式</title>
    <link href="http://yoursite.com/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/"/>
    <id>http://yoursite.com/观察者模式/</id>
    <published>2015-08-01T05:21:22.000Z</published>
    <updated>2018-06-23T15:24:29.019Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html" target="_blank" rel="noopener">http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html</a></p><ol><li><p>概述</p><p>　　有时被称作发布/订阅模式，观察者模式定义了一种一对多的依赖关系，让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时，会通知所有观察者对象，使它们能够自动更新自己。</p></li><li><p>解决的问题</p><p>　　将一个系统分割成一个一些类相互协作的类有一个不好的副作用，那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合，这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。</p></li><li><p>模式中的角色</p><p>　　3.1 抽象主题（Subject）：它把所有观察者对象的引用保存到一个聚集里，每个主题都可以有任何数量的观察者。抽象主题提供一个接口，可以增加和删除观察者对象。</p><p>　　3.2 具体主题（ConcreteSubject）：将有关状态存入具体观察者对象；在具体主题内部状态改变时，给所有登记过的观察者发出通知。</p><p>　　3.3 抽象观察者（Observer）：为所有的具体观察者定义一个接口，在得到主题通知时更新自己。</p><p>　　3.4 具体观察者（ConcreteObserver）：实现抽象观察者角色所要求的更新接口，以便使本身的状态与主题状态协调。</p></li><li><p>模式解读</p><p>　　4.1 观察者模式的类图　　</p><p>　　4.2 观察者模式的代码</p></li></ol><p>复制代码<br>///<br>/// 抽象主题类<br>///<br>public abstract class Subject<br>{<br>private IList observers = new List();</p><pre><code>    /// &lt;summary&gt;    /// 增加观察者    /// &lt;/summary&gt;    /// &lt;param name=&quot;observer&quot;&gt;&lt;/param&gt;    public void Attach(Observer observer)    {        observers.Add(observer);    }    /// &lt;summary&gt;    /// 移除观察者    /// &lt;/summary&gt;    /// &lt;param name=&quot;observer&quot;&gt;&lt;/param&gt;    public void Detach(Observer observer)    {        observers.Remove(observer);    }    /// &lt;summary&gt;    /// 向观察者（们）发出通知    /// &lt;/summary&gt;    public void Notify()    {        foreach (Observer o in observers)        {            o.Update();        }    }}/// &lt;summary&gt;/// 抽象观察者类，为所有具体观察者定义一个接口，在得到通知时更新自己/// &lt;/summary&gt;public abstract class Observer{    public abstract void Update();}/// &lt;summary&gt;/// 具体观察者或具体通知者，将有关状态存入具体观察者对象；在具体主题的内部状态改变时，给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。/// &lt;/summary&gt;public class ConcreteSubject : Subject{    private string subjectState;    /// &lt;summary&gt;    /// 具体观察者的状态    /// &lt;/summary&gt;    public string SubjectState    {        get { return subjectState; }        set { subjectState = value; }    }}/// &lt;summary&gt;/// 具体观察者，实现抽象观察者角色所要求的更新接口，已是本身状态与主题状态相协调/// &lt;/summary&gt;public class ConcreteObserver : Observer{    private string observerState;    private string name;    private ConcreteSubject subject;    /// &lt;summary&gt;    /// 具体观察者用一个具体主题来实现    /// &lt;/summary&gt;    public ConcreteSubject Subject    {        get { return subject; }        set { subject = value; }    }    public ConcreteObserver(ConcreteSubject subject, string name)    {        this.subject = subject;        this.name = name;    }    /// &lt;summary&gt;    /// 实现抽象观察者中的更新操作    /// &lt;/summary&gt;    public override void Update()    {        observerState = subject.SubjectState;        Console.WriteLine(&quot;The observer&apos;s state of {0} is {1}&quot;, name, observerState);    }}</code></pre><p>复制代码<br>　　4.3 客户端代码</p><p>复制代码<br>class Program<br>{<br>static void Main(string[] args)<br>{<br>// 具体主题角色通常用具体自来来实现<br>ConcreteSubject subject = new ConcreteSubject();</p><pre><code>        subject.Attach(new ConcreteObserver(subject, &quot;Observer A&quot;));        subject.Attach(new ConcreteObserver(subject, &quot;Observer B&quot;));        subject.Attach(new ConcreteObserver(subject, &quot;Observer C&quot;));        subject.SubjectState = &quot;Ready&quot;;        subject.Notify();        Console.Read();    }}</code></pre><p>复制代码<br>　　运行结果</p><ol><li><p>模式总结</p><p>　　5.1 优点</p><p>　　　　5.1.1 观察者模式解除了主题和具体观察者的耦合，让耦合的双方都依赖于抽象，而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。</p><p>　　5.2 缺点</p><p>　　　　5.2.1 依赖关系并未完全解除，抽象通知者依旧依赖抽象的观察者。</p><p>　　5.3 适用场景</p><p>　　　　5.3.1 当一个对象的改变需要给变其它对象时，而且它不知道具体有多少个对象有待改变时。</p><p>　　　　5.3.2 一个抽象某型有两个方面，当其中一个方面依赖于另一个方面，这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。</p></li><li><p>模式引申，应用C#中的事件委托来彻底解除通知者和观察者之间的耦合。</p><p>　　　6.1 关于委托的定义：委托是一种引用方法的类型。一旦为委托分配了方法，委托将与该方法有相同的行为。委托方法可以像其它任何方法一样，具有参数和返回值。委托可以看作是对函数（方法）的的抽象，是函数的“类”，委托的实例代表一个（或多个）具体的函数，它可以是多播的。</p><p>　　　6.2 关于事件：事件基于委托，为委托提供了一种发布/订阅机制。事件的订阅与取消与我们刚才讲的观察者模式中的订阅与取消类似，只是表现形式有所不同。在观察者模式中，订阅使用方法Attach（）来进行；在事件的订阅中使用“+=”。类似地，取消订阅在观察者模式中用Dettach（），而事件的取消用“-=”。</p></li><li><p>下面例子分别用观察者模式，事件机制来实现</p><p>　　7.1 实例描述：客户支付了订单款项，这时财务需要开具发票，出纳需要记账，配送员需要配货。</p><p>　　7.2 观察者模式的实现</p><p>　　　　7.2.1 类图</p><p>　　　　7.2.2 代码实现</p></li></ol><p>复制代码<br>///<br>/// 抽象观察者<br>///<br>public interface ISubject<br>{<br>void Notify();<br>}</p><pre><code>/// &lt;summary&gt;/// 工作岗位，作为这里的观察者的抽象/// &lt;/summary&gt;public abstract class JobStation{    public abstract void Update();}/// &lt;summary&gt;/// 具体主题，这里是客户/// &lt;/summary&gt;public class Customer : ISubject{    private string customerState;    private IList&lt;JobStation&gt; observers = new List&lt;JobStation&gt;();    /// &lt;summary&gt;    /// 增加观察者    /// &lt;/summary&gt;    /// &lt;param name=&quot;observer&quot;&gt;&lt;/param&gt;    public void Attach(JobStation observer)    {        this.observers.Add(observer);    }    /// &lt;summary&gt;    /// 移除观察者    /// &lt;/summary&gt;    /// &lt;param name=&quot;observer&quot;&gt;&lt;/param&gt;    public void Detach(JobStation observer)    {        this.observers.Remove(observer);    }    /// &lt;summary&gt;    /// 客户状态    /// &lt;/summary&gt;    public string CustomerState    {        get { return customerState; }        set { customerState = value; }    }    public void Notify()    {        foreach (JobStation o in observers)        {            o.Update();        }    }}/// &lt;summary&gt;/// 会计/// &lt;/summary&gt;public class Accountant : JobStation{    private string accountantState;    private Customer customer;    public Accountant(Customer customer)    {        this.customer = customer;    }    /// &lt;summary&gt;    /// 更新状态    /// &lt;/summary&gt;    public override void Update()    {        if (customer.CustomerState == &quot;已付款&quot;)        {            Console.WriteLine(&quot;我是会计，我来开具发票。&quot;);            accountantState = &quot;已开发票&quot;;        }    }}/// &lt;summary&gt;/// 出纳/// &lt;/summary&gt;public class Cashier : JobStation{    private string cashierState;    private Customer customer;    public Cashier(Customer customer)    {        this.customer = customer;    }    public override void Update()    {        if (customer.CustomerState == &quot;已付款&quot;)        {            Console.WriteLine(&quot;我是出纳员，我给登记入账。&quot;);            cashierState = &quot;已入账&quot;;        }    }}/// &lt;summary&gt;/// 配送员/// &lt;/summary&gt;public class Dilliveryman : JobStation{    private string dillivierymanState;    private Customer customer;    public Dilliveryman(Customer customer)    {        this.customer = customer;    }    public override void Update()    {        if (customer.CustomerState == &quot;已付款&quot;)        {            Console.WriteLine(&quot;我是配送员，我来发货。&quot;);            dillivierymanState = &quot;已发货&quot;;        }    }}</code></pre><p>复制代码<br>　　　　7.2.3 客户端代码</p><p>复制代码<br>class Program<br>{<br>static void Main(string[] args)<br>{</p><pre><code>        Customer subject = new Customer();        subject.Attach(new Accountant(subject));        subject.Attach(new Cashier(subject));        subject.Attach(new Dilliveryman(subject));        subject.CustomerState = &quot;已付款&quot;;        subject.Notify();        Console.Read();    }}</code></pre><p>复制代码<br>　　　　运行结果：</p><p>　　　　我是会计，我来开具发票。<br>　　　　我是出纳员，我给登记入账。<br>　　　　我是配送员，我来发货。</p><p>　　7.3 事件实现</p><p>　　　　7.3.1 类图</p><p>　　　　通过类图来看，观察者和主题之间已经不存在任何依赖关系了。</p><p>　　　　7.3.2 代码实现</p><p>复制代码<br>///<br>/// 抽象主题<br>///<br>public interface ISubject<br>{<br>void Notify();<br>}</p><pre><code>/// &lt;summary&gt;/// 声明委托/// &lt;/summary&gt;public delegate void CustomerEventHandler();/// &lt;summary&gt;/// 具体主题/// &lt;/summary&gt;public class Customer : ISubject{    private string customerState;    // 声明一个委托事件，类型为 CustomerEventHandler    public event CustomerEventHandler Update;    public void Notify()    {        if (Update != null)        {            // 使用事件来通知给订阅者            Update();        }    }    public string CustomerState    {        get { return customerState; }        set { customerState = value; }    }}/// &lt;summary&gt;/// 财务，已经不需要实现抽象的观察者类，并且不用引用具体的主题/// &lt;/summary&gt;public class Accountant{    private string accountantState;    public Accountant()    { }    /// &lt;summary&gt;    /// 开发票    /// &lt;/summary&gt;    public void GiveInvoice()    {        Console.WriteLine(&quot;我是会计，我来开具发票。&quot;);        accountantState = &quot;已开发票&quot;;    }}/// &lt;summary&gt;/// 出纳，已经不需要实现抽象的观察者类，并且不用引用具体的主题/// &lt;/summary&gt;public class Cashier{    private string cashierState;    public void Recoded()    {        Console.WriteLine(&quot;我是出纳员，我给登记入账。&quot;);        cashierState = &quot;已入账&quot;;    }}/// &lt;summary&gt;/// 配送员，已经不需要实现抽象的观察者类，并且不用引用具体的主题/// &lt;/summary&gt;public class Dilliveryman{    private string dillivierymanState;    public void Dilliver()    {        Console.WriteLine(&quot;我是配送员，我来发货。&quot;);        dillivierymanState = &quot;已发货&quot;;    }}</code></pre><p>复制代码<br>　　　　7.3.3 客户端代码</p><p>复制代码<br>class Program<br>{<br>static void Main(string[] args)<br>{</p><pre><code>        Customer subject = new Customer();        Accountant accountant = new Accountant();        Cashier cashier = new Cashier();        Dilliveryman dilliveryman = new Dilliveryman();        // 注册事件        subject.Update += accountant.GiveInvoice;        subject.Update += cashier.Recoded;        subject.Update += dilliveryman.Dilliver;        /*         * 以上写法也可以用下面代码来替换        subject.Update += new CustomerEventHandler(accountant.GiveInvoice);        subject.Update += new CustomerEventHandler(cashier.Recoded);        subject.Update += new CustomerEventHandler(dilliveryman.Dilliver);         */        subject.CustomerState = &quot;已付款&quot;;        subject.Notify();        Console.Read();    }}</code></pre><p>复制代码<br>　　　　运行结果</p><p>　　　　我是会计，我来开具发票。<br>　　　　我是出纳员，我给登记入账。<br>　　　　我是配送员，我来发货。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://www.cnblogs.com/wangjq/arch
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
  </entry>
  
  <entry>
    <title>单例模式</title>
    <link href="http://yoursite.com/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/"/>
    <id>http://yoursite.com/单例模式/</id>
    <published>2015-08-01T04:55:35.000Z</published>
    <updated>2018-06-23T15:24:28.881Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://cantellow.iteye.com/blog/838473" target="_blank" rel="noopener">http://cantellow.iteye.com/blog/838473</a></p><p>第一种（懒汉，线程不安全）：</p><p>Java代码 收藏代码<br>public class Singleton {<br>private static Singleton instance;<br>private Singleton (){}</p><pre><code>public static Singleton getInstance() {  if (instance == null) {      instance = new Singleton();  }  return instance;  }  </code></pre><p>}</p><p>这种写法lazy loading很明显，但是致命的是在多线程不能正常工作。<br>第二种（懒汉，线程安全）：</p><p>Java代码 收藏代码<br>public class Singleton {<br>private static Singleton instance;<br>private Singleton (){}<br>public static synchronized Singleton getInstance() {<br>if (instance == null) {<br>instance = new Singleton();<br>}<br>return instance;<br>}<br>}</p><p>这种写法能够在多线程中很好的工作，而且看起来它也具备很好的lazy loading，但是，遗憾的是，效率很低，99%情况下不需要同步。<br>第三种（饿汉）：</p><p>Java代码 收藏代码<br>public class Singleton {<br>private static Singleton instance = new Singleton();<br>private Singleton (){}<br>public static Singleton getInstance() {<br>return instance;<br>}<br>}</p><p>这种方式基于classloder机制避免了多线程的同步问题，不过，instance在类装载时就实例化，虽然导致类装载的原因有很多种，在单例模式中大多数都是调用getInstance方法， 但是也不能确定有其他的方式（或者其他的静态方法）导致类装载，这时候初始化instance显然没有达到lazy loading的效果。<br>第四种（饿汉，变种）：</p><p>Java代码 收藏代码<br>public class Singleton {<br>private Singleton instance = null;<br>static {<br>instance = new Singleton();<br>}<br>private Singleton (){}<br>public static Singleton getInstance() {<br>return this.instance;<br>}<br>}</p><p>表面上看起来差别挺大，其实更第三种方式差不多，都是在类初始化即实例化instance。<br>第五种（静态内部类）：</p><p>Java代码 收藏代码<br>public class Singleton {<br>private static class SingletonHolder {<br>private static final Singleton INSTANCE = new Singleton();<br>}<br>private Singleton (){}<br>public static final Singleton getInstance() {<br>return SingletonHolder.INSTANCE;<br>}<br>}</p><p>这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程，它跟第三种和第四种方式不同的是（很细微的差别）：第三种和第四种方式是只要Singleton类被装载了，那么instance就会被实例化（没有达到lazy loading效果），而这种方式是Singleton类被装载了，instance不一定被初始化。因为SingletonHolder类没有被主动使用，只有显示通过调用getInstance方法时，才会显示装载SingletonHolder类，从而实例化instance。想象一下，如果实例化instance很消耗资源，我想让他延迟加载，另外一方面，我不希望在Singleton类加载时就实例化，因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载，那么这个时候实例化instance显然是不合适的。这个时候，这种方式相比第三和第四种方式就显得很合理。<br>第六种（枚举）：</p><p>Java代码 收藏代码<br>public enum Singleton {<br>INSTANCE;<br>public void whateverMethod() {<br>}<br>}</p><p>这种方式是Effective Java作者Josh Bloch 提倡的方式，它不仅能避免多线程同步问题，而且还能防止反序列化重新创建新的对象，可谓是很坚强的壁垒啊，不过，个人认为由于1.5中才加入enum特性，用这种方式写不免让人感觉生疏，在实际工作中，我也很少看见有人这么写过。<br>第七种（双重校验锁）：<br>Java代码 收藏代码<br>public class Singleton {<br>private volatile static Singleton singleton;<br>private Singleton (){}<br>public static Singleton getSingleton() {<br>if (singleton == null) {<br>synchronized (Singleton.class) {<br>if (singleton == null) {<br>singleton = new Singleton();<br>}<br>}<br>}<br>return singleton;<br>}<br>}</p><p>这个是第二种方式的升级版，俗称双重检查锁定，详细介绍请查看：<a href="http://www.ibm.com/developerworks/cn/java/j-dcl.html" target="_blank" rel="noopener">http://www.ibm.com/developerworks/cn/java/j-dcl.html</a><br>在JDK1.5之后，双重检查锁定才能够正常达到单例效果。</p><p>总结<br>有两个问题需要注意：<br>1.如果单例由不同的类装载器装入，那便有可能存在多个单例类的实例。假定不是远端存取，例如一些servlet容器对每个servlet使用完全不同的类装载器，这样的话如果有两个servlet访问一个单例类，它们就都会有各自的实例。<br>2.如果Singleton实现了java.io.Serializable接口，那么这个类的实例就可能被序列化和复原。不管怎样，如果你序列化一个单例类的对象，接下来复原多个那个对象，那你就会有多个单例类的实例。<br>对第一个问题修复的办法是：</p><p>Java代码 收藏代码<br>private static Class getClass(String classname)<br>throws ClassNotFoundException {<br>ClassLoader classLoader = Thread.currentThread().getContextClassLoader();</p><pre><code>if(classLoader == null)        classLoader = Singleton.class.getClassLoader();     return (classLoader.loadClass(classname));     </code></pre><p>}<br>}<br>对第二个问题修复的办法是：</p><p>Java代码 收藏代码<br>public class Singleton implements java.io.Serializable {<br>public static Singleton INSTANCE = new Singleton();</p><p>protected Singleton() {</p><p>}<br>private Object readResolve() {<br>return INSTANCE;<br>}<br>}</p><h1 id="对我来说，我比较喜欢第三种和第五种方式，简单易懂，而且在JVM层实现了线程安全（如果不是多个类加载器环境），一般的情况下，我会使用第三种方式，只有在要明确实现lazy-loading效果时才会使用第五种方式，另外，如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例，不过，我一直会保证我的程序是线程安全的，而且我永远不会使用第一种和第二种方式，如果有其他特殊的需求，我可能会使用第七种方式，毕竟，JDK1-5已经没有双重检查锁定的问题了。"><a href="#对我来说，我比较喜欢第三种和第五种方式，简单易懂，而且在JVM层实现了线程安全（如果不是多个类加载器环境），一般的情况下，我会使用第三种方式，只有在要明确实现lazy-loading效果时才会使用第五种方式，另外，如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例，不过，我一直会保证我的程序是线程安全的，而且我永远不会使用第一种和第二种方式，如果有其他特殊的需求，我可能会使用第七种方式，毕竟，JDK1-5已经没有双重检查锁定的问题了。" class="headerlink" title="对我来说，我比较喜欢第三种和第五种方式，简单易懂，而且在JVM层实现了线程安全（如果不是多个类加载器环境），一般的情况下，我会使用第三种方式，只有在要明确实现lazy loading效果时才会使用第五种方式，另外，如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例，不过，我一直会保证我的程序是线程安全的，而且我永远不会使用第一种和第二种方式，如果有其他特殊的需求，我可能会使用第七种方式，毕竟，JDK1.5已经没有双重检查锁定的问题了。"></a>对我来说，我比较喜欢第三种和第五种方式，简单易懂，而且在JVM层实现了线程安全（如果不是多个类加载器环境），一般的情况下，我会使用第三种方式，只有在要明确实现lazy loading效果时才会使用第五种方式，另外，如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例，不过，我一直会保证我的程序是线程安全的，而且我永远不会使用第一种和第二种方式，如果有其他特殊的需求，我可能会使用第七种方式，毕竟，JDK1.5已经没有双重检查锁定的问题了。</h1><p>superheizai同学总结的很到位：</p><p>不过一般来说，第一种不算单例，第四种和第三种就是一种，如果算的话，第五种也可以分开写了。所以说，一般单例都是五种写法。懒汉，恶汉，双重校验锁，枚举和静态内部类。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;http://cantellow.iteye.com/blog/838473&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://cantellow.iteye.com/blog/838473&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;第一种（懒汉，
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
  </entry>
  
  <entry>
    <title>Android数字签名</title>
    <link href="http://yoursite.com/Android-Digital-Signature/"/>
    <id>http://yoursite.com/Android-Digital-Signature/</id>
    <published>2015-07-22T02:33:38.000Z</published>
    <updated>2018-08-07T07:16:47.675Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://www.oschina.net/question/157182_45890" target="_blank" rel="noopener">android数字签名</a></p><p>Android系统中，所有安装到系统的应用程序都必有一个数字证书，此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系,如果一个permission的protectionLevel为signature，那么就只有那些跟该permission所在的程序拥有同一个数字证书的应用程序才能取得该权限。Android使用Java的数字证书相关的机制来给apk加盖数字证书，要理解android的数字证书，需要先了解以下数字证书的概念和java的数字证书机制。Android系统要求每一个安装进系统的应用程序都是经过数字证书签名的，数字证书的私钥则保存在程序开发者的手中。Android将数字证书用来标识应用程序的作者和在应用程序之间建立信任关系，不是用来决定最终用户可以安装哪些应用程序。这个数字证书并不需要权威的数字证书签名机构认证，它只是用来让应用程序包自我认证的。<br>同一个开发者的多个程序尽可能使用同一个数字证书，这可以带来以下好处。</p><p>(1)有利于程序升级，当新版程序和旧版程序的数字证书相同时，Android系统才会认为这两个程序是同一个程序的不同版本。如果新版程序和旧版程序的数字证书不相同，则Android系统认为他们是不同的程序，并产生冲突，会要求新程序更改包名。</p><p>(2)有利于程序的模块化设计和开发。Android系统允许拥有同一个数字签名的程序运行在一个进程中，Android程序会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发，而用户只需要在需要的时候下载适当的模块。</p><p>(3)可以通过权限(permission)的方式在多个程序间共享数据和代码。Android提供了基于数字证书的权限赋予机制，应用程序可以和其他的程序共享概功能或者数据给那那些与自己拥有相同数字证书的程序。如果某个权限(permission)的protectionLevel是signature，则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。</p><p>在签名时，需要考虑数字证书的有效期：</p><p>(1)数字证书的有效期要包含程序的预计生命周期，一旦数字证书失效，持有改数字证书的程序将不能正常升级。</p><p>(2)如果多个程序使用同一个数字证书，则该数字证书的有效期要包含所有程序的预计生命周期。</p><p>(3)Android Market强制要求所有应用程序数字证书的有效期要持续到2033年10月22日以后。</p><p>Android数字证书包含以下几个要点：</p><pre><code>(1)所有的应用程序都必须有数字证书，Android系统不会安装一个没有数字证书的应用程序(2)Android程序包使用的数字证书可以是自签名的，不需要一个权威的数字证书机构签名认证(3)如果要正式发布一个Android ，必须使用一个合适的私钥生成的数字证书来给程序签名，而不能使用adt插件或者ant工具生成的调试证书来发布。(4)数字证书都是有有效期的，Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中，即使证书过期也不会影响程序的正常功能。(5)Android使用标准的java工具 Keytool and Jarsigner 来生成数字证书，并给应用程序包签名。（6）使用zipalign优化程序。</code></pre><p>Android系统不会安装运行任何一款未经数字签名的apk程序，无论是在模拟器上还是在实际的物理设备上。Android的开发工具(ADT插件和Ant)都可以协助开发者给apk程序签名，它们都有两种模式：调试模式(debug mode)和发布模式(release mode)。</p><p>在调试模式下，android的开发工具会在每次编译时使用调试用的数字证书给程序签名，开发者无须关心。</p><p>当要发布程序时，开发者就需要使用自己的数字证书给apk包签名，可以有两种方法。</p><p>(1)在命令行下使用JDK中的和Keytool(用于生成数字证书)和Jarsigner(用于使用数字证书签名)来给apk包签名。</p><p>(2)使用ADT Export Wizard进行签名(如果没有数字证书可能需要生成数字证书)。</p><p>使用Keytool和Jarsigner给程序签名</p><p>命令：keytool -genkey -v -keystore android.keystore -alias android -keyalg RSA -validity 20000</p><p>该命令中，-keystore ophone.keystore 表示生成的证书，可以加上路径（默认在用户主目录下）；-alias ophone 表示证书的别名是ophone；-keyalg RSA 表示采用的RSA算法；-validity 20000表示证书的有效期是20000天。</p><p>image</p><p>此时，我们会在互用主目录下看到ophone.keystore，即我们刚刚创建的证书。</p><p>接着对程序进行签名：</p><p>jarsigner用法： [选项] jar 文件别名<br>jarsigner -verify [选项] jar 文件</p><p>执行：jarsigner -verbose -keystore android.keystore -signedjar android123_signed.apk android123.apk android 就可以生成签名的apk文件，这里输入文件android123.apk，最终生成android123_signed.apk为Android签名后的APK执行文件。下面提示输入的密码和keytool输入的一样就行了。（不过在我的JDK目录下没有找到jarsigner这个程序，不知道是怎么回事）</p><p>image</p><p>使用ADT Export Wizard进行签名</p><p>应用程序（apk）签名,在EC中，右键单击应用程序工程，如图选择</p><p>image</p><p>选择证书的存放路径，填写相关资料，完成，即可生成被签名的apk文件。如下图所示：</p><p>image</p><p>如上图所示，我们可以看到也可以在这里选择”Create new keystore“来创建一个证书。输入密码，点击下一步，填写相关信息，如下图所示。</p><p>image</p><p>使用zipalign优化APK</p><p>根据官方文档的描述，Android系统中Application的数据都保存在它的APK文件中，同时可以被多个进程访问，安装的过程包括如下几个步骤：</p><p>Installer通过每个apk的manifest文件获取与当前应用程序相关联的permissions信息<br>Home application读取当前APK的Name和Icon等信息。<br>System server将读取一些与Application运行相关信息，例如：获取和处理Application的notifications请求等。<br>最后，APK所包含的内容不仅限于当前Application所使用，而且可以被其它的Application调用，提高系统资源的可复用性。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;http://www.oschina.net/question/157182_45890&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;android数字签名&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Android系统中，所有安装到系统的应用程序都必有一个
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="android" scheme="http://yoursite.com/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>Android 开发如何做内存优化</title>
    <link href="http://yoursite.com/Android%E5%BC%80%E5%8F%91%E5%A6%82%E4%BD%95%E5%81%9A%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96/"/>
    <id>http://yoursite.com/Android开发如何做内存优化/</id>
    <published>2015-07-22T00:50:06.000Z</published>
    <updated>2018-08-08T07:11:31.932Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>   不少人认为JAVA程序，因为有垃圾回收机制，应该没有内存泄露。其实如果我们一个程序中，已经不再使用某个对象，但是因为仍然有引用指向它，垃圾回收器就无法回收它，当然该对象占用的内存就无法被使用，这就造成了内存泄露。如果我们的java运行很久,而这种内存泄露不断的发生，最后就没内存可用了。当然java的，内存泄漏和C/C++是不一样的。如果java程序完全结束后，它所有的对象就都不可达了，系统就可以对他们进行垃圾回收，它的内存泄露仅仅限于它本身，而不会影响整个系统的。C/C++的内存泄露就比较糟糕了，它的内存泄露是系统级，即使该C/C++程序退出，它的泄露的内存也无法被系统回收，永远不可用了，除非重启机器。  </p><p>   Android的一个应用程序的内存泄露对别的应用程序影响不大。为了能够使得Android应用程序安全且快速的运行，Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行，它是由Zygote服务进程孵化出来的，也就是说每个应用程序都是在属于自己的进程中运行的。Android为不同类型的进程分配了不同的内存使用上限，如果程序在运行过程中出现了内存泄漏的而造成应用进程使用的内存超过了这个上限，则会被系统视为内存泄漏，从而被kill掉，这使得仅仅自己的进程被kill掉，而不会影响其他进程（如果是system_process等系统进程出问题的话，则会引起系统重启）。  </p><h1 id="引用没释放造成的内存泄露"><a href="#引用没释放造成的内存泄露" class="headerlink" title="引用没释放造成的内存泄露"></a>引用没释放造成的内存泄露</h1><h2 id="注册没取消造成的内存泄露"><a href="#注册没取消造成的内存泄露" class="headerlink" title="注册没取消造成的内存泄露"></a>注册没取消造成的内存泄露</h2><p>　　这种Android的内存泄露比纯java的内存泄露还要严重，因为其他一些Android程序可能引用我们的Anroid程序的对象（比如注册机制）。即使我们的Android程序已经结束了，但是别的引用程序仍然还有对我们的Android程序的某个对象的引用，泄露的内存依然不能被垃圾回收。<br>比如示例1:<br>　　假设我们希望在锁屏界面(LockScreen)中，监听系统中的电话服务以获取一些信息(如信号强度等)，则可以在LockScreen中定义一个PhoneStateListener的对象，同时将它注册到TelephonyManager服务中。对于LockScreen对象，当需要显示锁屏界面的时候就会创建一个LockScreen对象，而当锁屏界面消失的时候LockScreen对象就会被释放掉。<br>　　但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象，则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失，则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。<br>　　虽然有些系统程序，它本身好像是可以自动取消注册的（当然不及时），但是我们还是应该在我们的程序中明确的取消注册，程序结束时应该把所有的注册都取消掉。</p><h2 id="集合中对象没清理造成的内存泄露"><a href="#集合中对象没清理造成的内存泄露" class="headerlink" title="集合中对象没清理造成的内存泄露"></a>集合中对象没清理造成的内存泄露</h2><p>　　我们通常把一些对象的引用加入到了集合中，当我们不需要该对象时，并没有把它的引用从集合中清理掉，这样这个集合就会越来越大。如果这个集合是static的话，那情况就更严重了。  </p><h1 id="资源对象没关闭造成的内存泄露"><a href="#资源对象没关闭造成的内存泄露" class="headerlink" title="资源对象没关闭造成的内存泄露"></a>资源对象没关闭造成的内存泄露</h1><p>　　资源性对象比如（Cursor，File文件等）往往都用了一些缓冲，我们在不使用的时候，应该及时关闭它们，以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内，还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们，往往会造成内存泄露。因为有些资源性对象，比如SQLiteCursor（在析构函数finalize（）,如果我们没有关闭它，它自己会调close()关闭），如果我们没有关闭它，系统在回收它时也会关闭它，但是这样的效率太低了。因此对于资源性对象在不使用的时候，应该调用它的close()函数，将其关闭掉，然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。<br>　　程序中经常会进行查询数据库的操作，但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小，对内存的消耗不容易被发现，只有在常时间大量操作的情况下才会复现内存问题，这样就会给以后的测试和问题排查带来困难和风险。  </p><h1 id="一些不良代码成内存压力"><a href="#一些不良代码成内存压力" class="headerlink" title="一些不良代码成内存压力"></a>一些不良代码成内存压力</h1><p>　　有些代码并不造成内存泄露，但是它们，或是对没使用的内存没进行有效及时的释放，或是没有有效的利用已有的对象而是频繁的申请新内存，对内存的回收和分配造成很大影响的，容易迫使虚拟机不得不给该应用进程分配更多的内存，造成不必要的内存开支。  </p><h2 id="Bitmap没调用recycle"><a href="#Bitmap没调用recycle" class="headerlink" title="Bitmap没调用recycle()"></a>Bitmap没调用recycle()</h2><p>　　Bitmap对象在不使用时,我们应该先调用recycle()释放内存，然后才它设置为null.虽然recycle()从源码上看，调用它应该能立即释放Bitmap的主要内存，但是测试结果显示它并没能立即释放内存。但是我它应该还是能大大的加速Bitmap的主要内存的释放。 </p><h2 id="构造Adapter时，没有使用缓存的-convertView"><a href="#构造Adapter时，没有使用缓存的-convertView" class="headerlink" title="构造Adapter时，没有使用缓存的 convertView"></a>构造Adapter时，没有使用缓存的 convertView</h2><p>　　以构造ListView的BaseAdapter为例，在BaseAdapter中提共了方法：<br>public View getView(int position, View convertView, ViewGroup parent)来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象，同时ListView会将这些view对象缓存起来。当向上滚动ListView时，原先位于最上面的list item的view对象会被回收，然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的，getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。<br>　　由此可以看出，如果我们不去使用convertView，而是每次都在getView()中重新实例化一个View对象的话，即浪费时间，也造成内存垃圾，给垃圾回收增加压力，如果垃圾回收来不及的话，虚拟机将不得不给该应用进程分配更多的内存，造成不必要的内存开支。ListView回收list item的view对象的过程可以查看:<br>view plaincopy to clipboardprint?<br>android.widget.AbsListView.java –&gt; void addScrapView(View scrap) 方法。</p><p>示例代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span> <span class="function"><span class="keyword">public</span> View <span class="title">getView</span><span class="params">(<span class="keyword">int</span> position, View convertView, ViewGroup parent)</span> </span>&#123;  </span><br><span class="line"><span class="number">2</span>  </span><br><span class="line"><span class="number">3</span> View view = <span class="keyword">new</span> Xxx(…);  </span><br><span class="line"><span class="number">4</span>  </span><br><span class="line"><span class="number">5</span> … …  </span><br><span class="line"><span class="number">6</span>  </span><br><span class="line"><span class="number">7</span> <span class="keyword">return</span> view;  </span><br><span class="line"><span class="number">8</span>  </span><br><span class="line"><span class="number">9</span> &#125;</span><br></pre></td></tr></table></figure><p>修正示例代码：</p><p>Android内存管理<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span> <span class="function"><span class="keyword">public</span> View <span class="title">getView</span><span class="params">(<span class="keyword">int</span> position, View convertView, ViewGroup parent)</span> </span>&#123;  </span><br><span class="line"><span class="number">2</span>  </span><br><span class="line"><span class="number">3</span> View view = <span class="keyword">null</span>;  </span><br><span class="line"><span class="number">4</span>  </span><br><span class="line"><span class="number">5</span> <span class="keyword">if</span> (convertView != <span class="keyword">null</span>) &#123;  </span><br><span class="line"><span class="number">6</span>  </span><br><span class="line"><span class="number">7</span> view = convertView;  </span><br><span class="line"><span class="number">8</span>  </span><br><span class="line"><span class="number">9</span> populate(view, getItem(position));  </span><br><span class="line"><span class="number">10</span>  </span><br><span class="line"><span class="number">11</span> …  </span><br><span class="line"><span class="number">12</span>  </span><br><span class="line"><span class="number">13</span> &#125; <span class="keyword">else</span> &#123;  </span><br><span class="line"><span class="number">14</span>  </span><br><span class="line"><span class="number">15</span> view = <span class="keyword">new</span> Xxx(…);  </span><br><span class="line"><span class="number">16</span>  </span><br><span class="line"><span class="number">17</span> …  </span><br><span class="line"><span class="number">18</span>  </span><br><span class="line"><span class="number">19</span> &#125;  </span><br><span class="line"><span class="number">20</span>  </span><br><span class="line"><span class="number">21</span> <span class="keyword">return</span> view;  </span><br><span class="line"><span class="number">22</span>  </span><br><span class="line"><span class="number">23</span> &#125;</span><br></pre></td></tr></table></figure></p><p>概述：<br>　　在android的开发中，要时刻主要内存的分配和垃圾回收，因为系统为每一个dalvik虚拟机分配的内存是有限的，在google的G1中，分配的最大堆大小只有16M，后来的机器一般都为24M，实在是少的可怜。这样就需要我们在开发过程中要时刻注意。不要因为自己的代码问题而造成OOM错误。  </p><pre><code>JAVA的内存管理：  </code></pre><p>　　大家都知道，android应用层是由java开发的，android的davlik虚拟机与jvm也类似，只不过它是基于寄存器的。因此要了解android的内存管理就必须得了解java的内存分配和垃圾回收机制。<br>　　在java中，是通过new关键字来为对象分配内存的，而内存的释放是由垃圾收集器（GC）来回收的，工程师在开发的过程中，不需要显式的去管理内存。但是这样有可能在不知不觉中就会浪费了很多内存，最终导致java虚拟机花费很多时间去进行垃圾回收，更严重的是造成JVM的OOM。因此，java工程师还是有必要了解JAVA的内存分配和垃圾回收机制。</p><p>内存结构<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-6926.png<br>上面这张图是JVM的结构图，它主要四个部分组成：Class Loader子系统和执行引擎，运行时方法区和本地方法区，我们主要来看下RUNTIME DATA AREA区，也就是我们常说的JVM内存。从图中可以看出，RUNTIMEDATA AREA区主要由5个部分组成：<br>· Method Area:被装载的class的元信息存储在Method Area中，它是线程共享的<br>· Heap(堆)：一个java虚拟机实例中只存在一个堆空间，存放一些对象信息，它是线程共享的<br>· Java栈： java虚拟机直接对java栈进行两种操作，以帧为单位的压栈和出栈（非线程共享）<br>· 程序计数器（非线程共享）<br>· 本地方法栈（非线程共享）</p><p>JVM的垃圾回收（GC）<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-12485.png<br>JVM的垃圾原理是这样的，它把对象分为年轻代（Young）、年老代（Tenured）、持久代（Perm），对不同生命周期的对象使用不同的垃圾回收算法。<br>· 年轻代(Young)<br>年轻代分为三个区，一个eden区，两个Survivor区。程序中生成的大部分新的对象都在Eden区中，当Eden区满时，还存活的对象将被复制到其中一个Survivor区，当此Survivor区的对象占用空间满了时，此区存活的对象又被复制到另外一个Survivor区，当这个Survivor区也满了的时候，从第一个Survivor区复制过来的并且此时还存活的对象，将被复制到年老代。<br>· 年老代（Tenured）<br>年老代存放的是上面年轻代复制过来的对象，也就是在年轻代中还存活的对象，并且区满了复制过来的。一般来说，年老代中的对象生命周期都比较长。<br>· 持久代（Perm）<br>用于存放静态的类和方法，持久代对垃圾回收没有显著的影响。<br>Android中内存泄露监测<br>在了解了JVM的内存管理后，我们再回过头来看看，在android中应该怎样来监测内存，从而看在应用中是否存在内存分配和垃圾回收问题而造成内存泄露情况。<br>在android中，有一个相对来说还不错的工具，可以用来监测内存是否存在泄露情况：DDMS—Heap<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-22715.png<br>使用方法比较简单：<br>· 选择DDMS视图，并打开Devices视图和Heap视图<br>· 点击选择要监控的进程，比如：上图中我选择的是system_process<br>· 选中Devices视图界面上的”update heap” 图标<br>· 点击Heap视图中的”Cause GC” 按钮（相当于向虚拟机发送了一次GC请求的操作）<br>在Heap视图中选择想要监控的Type，一般我们会观察dataobject的 total size的变化，正常情况下total size的值会稳定在一个有限的范围内，也就说程序中的代码良好，没有造成程序中的对象不被回收的情况。如果代码中存在没有释放对象引用的情况，那么data object的total size在每次GC之后都不会有明显的回落，随着操作次数的增加而total size也在不断的增加。（说明：选择好data object后，不断的操作应用，这样才可以看出total size的变化）。如果totalsize确实是在不断增加而没有回落，说明程序中有没有被释放的资源引用。那么我们应该怎么来定位呢？<br>Android中内存泄露定位<br>Mat(memory analyzer tools)是我们常用的用来定位内存泄露的工具，如果你使用ADT，并且安装了MAT的eclipse插件，你需要做的是进入DDMS视图的Devices视图：<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-2165.png<br>点击”dump HPROF file”按钮，然后使用MAT分析下载下来的文件。<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-6565.png</p><p>下面列出了存在的问题，点击detail进去，会列出详细的，可能会存在问题的代码：<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-32625.png<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-21158.png<br>关于MAT的使用可以参考：<a href="http://www.blogjava.net/rosen/" target="_blank" rel="noopener">http://www.blogjava.net/rosen/</a> … .html<br>这位兄弟写的比较详细。</p><p>总结</p><p>不管是java还是android，都应该了解内存分配和垃圾回收机制，工程师要做到写的代码中没有bad code很难，关键是在出现问题的时候该怎么去排查Android内存优化  </p><p>一、 Android的内存机制<br>　　Android的程序由Java语言编写，所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存，所有对象在java堆内分配空间；然而对象的释放是由垃圾回收器来完成的。C／C++中的内存机制是“谁污染，谁治理”，java的就比较人性化了，给我们请了一个专门的清洁工（GC）。<br>　　那么GC怎么能够确认某一个对象是不是已经被废弃了呢？Java采用了有向图的原理。Java将引用关系考虑为图的有向边，有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点，该图就是从起始顶点开始的一棵树，根顶点可以到达的对象都是有效对象，GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意，该图为有向图)，那么我们认为这个(这些)对象不再被引用，可以被GC回收。<br>二、Android的内存溢出<br>Android的内存溢出是如何发生的?<br>　　Android的虚拟机是基于寄存器的Dalvik，它的最大堆大小一般是16M，有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。<br>为什么会出现内存不够用的情况呢？我想原因主要有两个：</p><p>由于我们程序的失误，长期保持某些资源（如Context）的引用，造成内存泄露，资源造成得不到释放。</p><p>保存了多个耗用内存过大的对象（如Bitmap），造成内存超出限制。</p><p>三、万恶的static<br>　　static是Java中的一个关键字，当用它来修饰成员变量时，那么该变量就属于该类，而不是该类的实例。所以用static修饰的变量，它的生命周期是很长的，如果用它来引用一些资源耗费过多的实例（Context的情况最多），这时就要谨慎对待了。</p><pre><code class="java"><span class="number">1</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassName</span> </span>{  <span class="number">2</span>  <span class="number">3</span> <span class="keyword">private</span> <span class="keyword">static</span> Context mContext;  <span class="number">4</span>  <span class="number">5</span> <span class="comment">//省略  </span><span class="number">6</span>  <span class="number">7</span> }  </code></pre><p>　　以上的代码是很危险的，如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy，但是由于仍有对象保存它的引用，因此该Activity依然不会被释放。<br>我们举Android文档中的一个例子。</p><pre><code class="java"><span class="keyword">private</span> <span class="keyword">static</span> Drawable sBackground;<span class="meta">@Override</span><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle state)</span> </span>{<span class="keyword">super</span>.onCreate(state);TextView label = <span class="keyword">new</span> TextView(<span class="keyword">this</span>);label.setText(“Leaks are bad”);<span class="keyword">if</span> (sBackground == <span class="keyword">null</span>) {    sBackground = getDrawable(R.drawable.large_bitmap);  }label.setBackgroundDrawable(sBackground);setContentView(label);}  </code></pre><p>　　sBackground是一个静态的变量，但是我们发现，我们并没有显式的保存Contex的引用，但是，当Drawable与View连接之后，Drawable就将View设置为一个回调，由于View中是包含Context的引用的，所以，实际上我们依然保存了Context的引用。这个引用链如下：<br>Drawable-&gt;TextView-&gt;Context<br>　　所以，最终该Context也没有得到释放，发生了内存泄露。<br>如何才能有效的避免这种引用的发生呢？</p><p>应该尽量避免static成员变量引用资源耗费过多的实例，比如Context。</p><p>Context尽量使用Application Context，因为Application的Context的生命周期比较长，引用它不会出现内存泄露的问题。</p><p>使用WeakReference代替强引用。比如可以使用WeakReference mContextRef;</p><p>该部分的详细内容也可以参考Android文档中Article部分。<br>四、都是线程惹的祸<br>　　线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。</p><pre><code class="java"><span class="number">1</span> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>{  <span class="number">2</span>  <span class="number">3</span> <span class="meta">@Override</span>  <span class="number">4</span>  <span class="number">5</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>{  <span class="number">6</span>  <span class="number">7</span> <span class="keyword">super</span>.onCreate(savedInstanceState);  <span class="number">8</span>  <span class="number">9</span> setContentView(R.layout.main);  <span class="number">10</span>  <span class="number">11</span> <span class="keyword">new</span> MyThread().start();  <span class="number">12</span>  <span class="number">13</span> }  <span class="number">14</span>  <span class="number">15</span>  <span class="number">16</span> <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span></span>{  <span class="number">17</span>  <span class="number">18</span> <span class="meta">@Override</span>  <span class="number">19</span>  <span class="number">20</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{  <span class="number">21</span>  <span class="number">22</span> <span class="keyword">super</span>.run();  <span class="number">23</span>  <span class="number">24</span> <span class="comment">//do somthing  </span><span class="number">25</span>  <span class="number">26</span> }  <span class="number">27</span>  <span class="number">28</span> }  <span class="number">29</span>  <span class="number">30</span> }  </code></pre><p>　　这段代码很平常也很简单，是我们经常使用的形式。我们思考一个问题：假设MyThread的run函数是一个很费时的操作，当我们开启该线程后，将设备的横屏变为了竖屏，一般情况下当屏幕转换时会重新创建Activity，按照我们的想法，老的Activity应该会被销毁才对，然而事实上并非如此。<br>　　由于我们的线程是Activity的内部类，所以MyThread中保存了Activity的一个引用，当MyThread的run函数没有结束时，MyThread是不会被销毁的，因此它所引用的老的Activity也不会被销毁，因此就出现了内存泄露的问题。<br>file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ksohtml/wps_clip_image-6439.png<br>　　有些人喜欢用Android提供的AsyncTask，但事实上AsyncTask的问题更加严重，Thread只有在run函数不结束时才出现这种内存泄露问题，然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的，是应用程序无法控制的，因此如果AsyncTask作为Activity的内部类，就更容易出现内存泄露的问题。<br>这种线程导致的内存泄露问题应该如何解决呢？</p><p>将线程的内部类，改为静态内部类。</p><p>在线程内部采用弱引用保存Context引用。</p><p>解决的模型如下：</p><p>复制代码<br>1 public abstract class WeakAsyncTask</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://www.cnblogs.com/kingOfPointer/archive/2012/12/21/2828018.html" target="_blank" rel="noopener">android 开发如何做内存优化</a></p><p><a href="http://zmywly8866.github.io/2016/05/04/android-application-leak-analysis-and-fix.html" target="_blank" rel="noopener">Android应用内存泄露分析、改善经验总结</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;   不少人认为JAVA程序，因为有垃圾回收机制，应该没有内存泄露。其实如果我们一个程序中，已经不再使用某个对象，但是因为仍然有引用指向它，
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android 内存" scheme="http://yoursite.com/tags/Android-%E5%86%85%E5%AD%98/"/>
    
  </entry>
  
  <entry>
    <title>Linux脚本编写基础</title>
    <link href="http://yoursite.com/Linux-Command/"/>
    <id>http://yoursite.com/Linux-Command/</id>
    <published>2015-06-29T06:23:08.000Z</published>
    <updated>2018-07-30T07:17:44.513Z</updated>
    
    <content type="html"><![CDATA[<p>本文主要介绍shell脚本编写的基础语法，开头、注释、变量和 环境变量，虽然不涉及具体东西，但打好基础确是最为关键的。<br>本文是一篇在学习中的文章，还没学习完，排版也还有一些问题，待继续</p><h1 id="shell脚本编写初步介绍"><a href="#shell脚本编写初步介绍" class="headerlink" title="shell脚本编写初步介绍"></a>shell脚本编写初步介绍</h1><p>##开头程序必须以下面的行开始（必须方在文件的第一行）：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br></pre></td></tr></table></figure></p><p>符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。<br>当编辑好脚本时，如果要执行该脚本，还必须使其可执行。<br>要使脚本可执行：<br>编译 chmod +x filename 这样才能用./filename 来运行  </p><h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><p>　　以#开头的句子表示注释，直到这一行的结束。  </p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>　　在其他编程语言中您必须使用变量。在shell编程中，所有的变量都由字符串组成，并且您不需要对变量进行声明。要赋值给一个变量，您可以这样写：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment">#对变量赋值：</span></span><br><span class="line">a=<span class="string">"hello world"</span> 首先输入的应该是包含减号的参数.</span><br><span class="line"><span class="comment"># 现在打印变量a的内容： </span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"A is:"</span> </span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$a</span></span><br></pre></td></tr></table></figure></p><p>有时候变量名很容易与其他文字混淆，比如：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">num=2</span><br><span class="line"><span class="built_in">echo</span> <span class="string">"this is the <span class="variable">$numnd</span>"</span></span><br></pre></td></tr></table></figure></p><p>这并不会打印出”this is the 2nd”，而仅仅打印”this is the “，因为shell会去搜索变量numnd的值，但是这个变量时没有值的。可以使用花括号来告诉shell我们要打印的是num变量：</p><p>num=2<br>echo “this is the {num}nd”</p><p>这将打印： this is the 2nd</p><h2 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h2><p>由export关键字处理过的变量叫做环境变量。我们不对环境变量进行讨论，因为通常情况下仅仅在登录脚本中使用环境变量。</p><h2 id="Shell命令和流程控制"><a href="#Shell命令和流程控制" class="headerlink" title="Shell命令和流程控制"></a>Shell命令和流程控制</h2><p>在shell脚本中可以使用三类命令：     </p><h3 id="1-Unix-命令"><a href="#1-Unix-命令" class="headerlink" title="1)Unix 命令:"></a>1)Unix 命令:</h3><p>　　虽然在shell脚本中可以使用任意的unix命令，但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。<br>常用命令语法及功能<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="built_in">echo</span> “some text”: 将文字内容打印在屏幕上     </span><br><span class="line">　　ls: 文件列表     </span><br><span class="line">　　wc –l filewc -w filewc -c file: 计算文件行数计算文件中的单词数计算文件中的字符数     </span><br><span class="line">　　cp sourcefile destfile: 文件拷贝     </span><br><span class="line">　　mv oldname newname : 重命名文件或移动文件     </span><br><span class="line">　　rm file: 删除文件     </span><br><span class="line">　　grep ‘pattern’ file: 在文件内搜索字符串比如：grep ‘searchstring’ file.txt     </span><br><span class="line">　　cut -b colnum file: 指定欲显示的文件内容范围，并将它们输出到标准输出设备比如：输出每行第5个到第9个字符cut -b5-9 file.txt千万不要和cat命令混淆,这是两个完全不同的命令     </span><br><span class="line">　　cat file.txt: 输出文件内容到标准输出设备（屏幕）上     </span><br><span class="line">　　file somefile: 得到文件类型     </span><br><span class="line">　　<span class="built_in">read</span> var: 提示用户输入，并将输入赋值给变量     </span><br><span class="line">　　sort file.txt: 对file.txt文件中的行进行排序     </span><br><span class="line">　　uniq: 删除文本文件中出现的行列比如： sort file.txt | uniq     </span><br><span class="line">　　expr: 进行数学运算Example: add 2 and 3expr 2 “+” 3     </span><br><span class="line">　　find: 搜索文件比如：根据文件名搜索find . -name filename -<span class="built_in">print</span>     </span><br><span class="line">　　tee: 将数据输出到标准输出设备(屏幕) 和文件比如：somecommand | tee outfile     </span><br><span class="line">　　basename file: 返回不包含路径的文件名比如： basename /bin/tux将返回 tux     </span><br><span class="line">　　dirname file: 返回文件所在路径比如：dirname /bin/tux将返回 /bin     </span><br><span class="line">　　head file: 打印文本文件开头几行     </span><br><span class="line">　　tail file : 打印文本文件末尾几行     </span><br><span class="line">　　sed: Sed是一个基本的查找替换程序。可以从标准输入（比如命令管道）读入文本，并将结果输出到标准输出（屏幕）。该命令采用正则表达式（见参考）进行搜索。不要和shell中的通配符相混淆。比如：将linuxfocus 替换为LinuxFocus ：cat text.file | sed ‘s/linuxfocus/LinuxFocus/’ &gt; newtext.file     </span><br><span class="line">　　awk: awk 用来从文本文件中提取字段。缺省地，字段分割符是空格，可以使用-F指定其他分割符。</span><br></pre></td></tr></table></figure></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat file.txt | awk -F, <span class="string">'&#123;print $1 "," $3 &#125;'</span></span><br></pre></td></tr></table></figure><p>这里我们使用，作为字段分割符，同时打印第一个和第三个字段。如果该文件内容如下： Adam Bor, 34, IndiaKerry Miller, 22, USA</p><h3 id="2-概念-管道-重定向和-backtick-这些不是系统命令，但是他们真的很重要。"><a href="#2-概念-管道-重定向和-backtick-这些不是系统命令，但是他们真的很重要。" class="headerlink" title="2) 概念: 管道, 重定向和 backtick 这些不是系统命令，但是他们真的很重要。"></a>2) 概念: 管道, 重定向和 backtick 这些不是系统命令，但是他们真的很重要。</h3><p>　　管道 (|) 将一个命令的输出作为另外一个命令的输入。<br>grep “hello” file.txt | wc -l<br>　　在file.txt中搜索包含有”hello”的行并计算其行数。<br>　　在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。<br>　　重定向：将命令的结果输出到文件，而不是标准输出（屏幕）。<br>　　> 写入文件并覆盖旧文件<br>　　&gt;> 加到文件的尾部，保留旧文件内容。<br>反短斜线<br>　 使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。<br>命令： </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">find . -mtime -1 -<span class="built_in">type</span> f -<span class="built_in">print</span></span><br></pre></td></tr></table></figure><p>　　用来查找过去24小时（-mtime –2则表示过去48小时）内修改过的文件。如果您想将所有查找到的文件打一个包，则可以使用以下脚本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment"># The ticks are backticks (`) not normal quotes ('):</span></span><br><span class="line">tar -zcvf lastmod.tar.gz `find . -mtime -1 -<span class="built_in">type</span> f -<span class="built_in">print</span>`</span><br></pre></td></tr></table></figure><h3 id="3-流程控制"><a href="#3-流程控制" class="headerlink" title="3) 流程控制"></a>3) 流程控制</h3><ol><li>if<br>　　“if” 表达式 如果条件为真则执行then后面的部分：</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ….; <span class="keyword">then</span>  </span><br><span class="line">　 ….  </span><br><span class="line"><span class="keyword">elif</span> ….; <span class="keyword">then</span>  </span><br><span class="line">　 ….  </span><br><span class="line"><span class="keyword">else</span>  </span><br><span class="line">　 ….  </span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>大多数情况下，可以使用测试命令来对条件进行测试。比如可以比较字符串、判断文件是否存在及是否可读等等…<br>　　通常用” [ ] “来表示条件测试。注意这里的空格很重要。要确保方括号的空格。<br>[ -f “somefile” ] ：判断是否是一个文件<br>[ -x “/bin/ls” ] ：判断/bin/ls是否存在并有可执行权限<br>[ -n “var”]：判断var” ] ：判断var变量是否有值<br>[ “a”=”a” = “b” ] ：判断a和a和b是否相等<br>　　执行man test可以查看所有测试表达式可以比较和判断的类型。<br>　　直接执行以下脚本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">"<span class="variable">$SHELL</span>"</span> = <span class="string">"/bin/bash"</span> ]; <span class="keyword">then</span></span><br><span class="line">　<span class="built_in">echo</span> <span class="string">"your login shell is the bash (bourne again shell)"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">　<span class="built_in">echo</span> <span class="string">"your login shell is not bash but <span class="variable">$SHELL</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line">变量<span class="variable">$SHELL</span>包含了登录shell的名称，我们和/bin/bash进行了比较。  </span><br><span class="line">快捷操作符  </span><br><span class="line">熟悉C语言的朋友可能会很喜欢下面的表达式：  </span><br><span class="line">\[ -f “/etc/shadow” \] &amp;&amp; <span class="built_in">echo</span> “This computer uses shadow passwors”  </span><br><span class="line">　　这里 &amp;&amp; 就是一个快捷操作符，如果左边的表达式为真则执行右边的语句。您也可以认为是逻辑运算中的与操作。上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。同样或操作(||)在shell编程中也是可用的。这里有个例子：</span><br><span class="line"></span><br><span class="line">``` bash</span><br><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">mailfolder=/var/spool/mail/james</span><br><span class="line">[ -r <span class="string">"<span class="variable">$mailfolder</span>"</span> ]<span class="string">' '</span>&#123; <span class="built_in">echo</span> <span class="string">"Can not read <span class="variable">$mailfolder</span>"</span> ; <span class="built_in">exit</span> 1; &#125;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="variable">$mailfolder</span> has mail from:"</span></span><br><span class="line">grep <span class="string">"^From "</span> <span class="variable">$mailfolder</span></span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">该脚本首先判断mailfolder是否可读。如果可读则打印该文件中的”From” 一行。如果不可读则或操作生效，打印错误信息后脚本退出。这里有个问题，那就是我们必须有两个命令：  </span><br><span class="line">　　-打印错误信息  </span><br><span class="line">　　-退出程序  </span><br><span class="line">　　我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用。一般函数将在下文提及。  </span><br><span class="line">　　不用与和或操作符，我们也可以用<span class="keyword">if</span>表达式作任何事情，但是使用与或操作符会更便利很多。</span><br><span class="line"></span><br><span class="line">2. <span class="keyword">case</span>  </span><br><span class="line"><span class="keyword">case</span> :表达式可以用来匹配一个给定的字符串，而不是数字。 </span><br><span class="line"></span><br><span class="line">``` bash </span><br><span class="line"><span class="keyword">case</span> … <span class="keyword">in</span>  </span><br><span class="line">…) <span class="keyword">do</span> something here ;;  </span><br><span class="line"><span class="keyword">esac</span></span><br></pre></td></tr></table></figure><p>　　让我们看一个例子。 file命令可以辨别出一个给定文件的文件类型，比如：  </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">file lf.gz</span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line">　　这将返回：  </span><br><span class="line"></span><br><span class="line">``` bash</span><br><span class="line">lf.gz: gzip compressed data, deflated, original filename, last modified: Mon Aug 27 23:09:18 2001, os: Unix</span><br></pre></td></tr></table></figure><p>　我们利用这一点写了一个叫做smartzip的脚本，该脚本可以自动解压bzip2, gzip 和zip 类型的压缩文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">ftype=`file <span class="string">"<span class="variable">$1</span>"</span>`</span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$ftype</span>"</span> <span class="keyword">in</span></span><br><span class="line"><span class="string">"<span class="variable">$1</span>: Zip archive"</span>*)</span><br><span class="line">　　unzip <span class="string">"<span class="variable">$1</span>"</span> ;;</span><br><span class="line"><span class="string">"<span class="variable">$1</span>: gzip compressed"</span>*)</span><br><span class="line">　　gunzip <span class="string">"<span class="variable">$1</span>"</span> ;;</span><br><span class="line"><span class="string">"<span class="variable">$1</span>: bzip2 compressed"</span>*)</span><br><span class="line">　　bunzip2 <span class="string">"<span class="variable">$1</span>"</span> ;;</span><br><span class="line">*) <span class="built_in">echo</span> <span class="string">"File <span class="variable">$1</span> can not be uncompressed with smartzip"</span>;;</span><br><span class="line"><span class="keyword">esac</span></span><br></pre></td></tr></table></figure><p>　　您可能注意到我们在这里使用了一个特殊的变量1。该变量包含了传递给该程序的第一个参数值。也就是说，当我们运行：smartziparticles.zip1。该变量包含了传递给该程序的第一个参数值。 也就是说，当我们运行：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartzip articles.zip</span><br></pre></td></tr></table></figure></p><p> $1 就是字符串 articles.zip </p><ol start="3"><li>selsect<br>select 表达式是一种bash的扩展应用，尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">select var <span class="keyword">in</span> … ; <span class="keyword">do</span>  </span><br><span class="line">　<span class="built_in">break</span>  </span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure></li></ol><p>…. now $var can be used ….<br>下面是一个例子：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"What is your favourite OS?"</span></span><br><span class="line">select var <span class="keyword">in</span> <span class="string">"Linux"</span> <span class="string">"Gnu Hurd"</span> <span class="string">"Free BSD"</span> <span class="string">"Other"</span>; <span class="keyword">do</span></span><br><span class="line">　　　　<span class="built_in">break</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"You have selected <span class="variable">$var</span>"</span></span><br></pre></td></tr></table></figure></p><p>　　下面是该脚本运行的结果：<br>What is your favourite OS?<br>1) Linux<br>2) Gnu Hurd<br>3) Free BSD<br>4) Other</p><p>? 1</p><p>You have selected Linux </p><ol start="4"><li>loop<br>loop表达式： <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> …; <span class="keyword">do</span>  </span><br><span class="line">….  </span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure></li></ol><p>　　while-loop 将运行直到表达式测试为真。will run while the expression that we test for is true.<br>关键字”break” 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。<br>　　<br>for-loop表达式查看一个字符串列表 (字符串用空格分隔) 然后将其赋给一个变量：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> var <span class="keyword">in</span> ….; <span class="keyword">do</span>  </span><br><span class="line">　 ….  </span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure></p><p>在下面的例子中，将分别打印ABC到屏幕上：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="keyword">for</span> var <span class="keyword">in</span> A B C ; <span class="keyword">do</span></span><br><span class="line">　 <span class="built_in">echo</span> <span class="string">"var is <span class="variable">$var</span>"</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure></p><p>下面是一个更为有用的脚本showrpm，其功能是打印一些RPM包的统计信息：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment"># list a content summary of a number of RPM packages</span></span><br><span class="line"><span class="comment"># USAGE: showrpm rpmfile1 rpmfile2 ...</span></span><br><span class="line"><span class="comment"># EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm</span></span><br><span class="line"><span class="keyword">for</span> rpmpackage <span class="keyword">in</span> $*; <span class="keyword">do</span></span><br><span class="line">　<span class="keyword">if</span> [ -r <span class="string">"<span class="variable">$rpmpackage</span>"</span> ];<span class="keyword">then</span></span><br><span class="line">　　<span class="built_in">echo</span> <span class="string">"=============== <span class="variable">$rpmpackage</span> =============="</span></span><br><span class="line">　　rpm -qi -p <span class="variable">$rpmpackage</span></span><br><span class="line">　<span class="keyword">else</span></span><br><span class="line">　　<span class="built_in">echo</span> <span class="string">"ERROR: cannot read file <span class="variable">$rpmpackage</span>"</span></span><br><span class="line">　<span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure></p><p>这里出现了第二个特殊的变量$*，该变量包含了所有输入的命令行参数值。<br>如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm<br>此时 $* 包含了 3 个字符串，即openssh.rpm, w3m.rpm and webgrep.rpm.</p><ol start="5"><li><p>引号<br>在向程序传递任何参数之前，程序会扩展通配符和变量。这里所谓扩展的意思是程序会把通配符比如(*）替换成合适的文件名，它变量替换成变量值。为了防止程序作这种替换，您可以使用引号：让我们来看一个例子，假设在当前目录下有一些文件，两个jpg文件， mail.jpg 和tux.jpg。<br>1.编译SHELL脚本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ch<span class="comment">#!/bin/sh mod +x filename</span></span><br></pre></td></tr></table></figure><p>./filename 来执行您的脚本。<br>　　这将打印出”mail.jpg tux.jpg”的结果。<br>　  引号 (单引号和双引号) 将防止这种通配符扩展：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"*.jpg"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">'*.jpg'</span></span><br><span class="line">``` </span><br><span class="line">　　这将打印”\*.jpg” 两次。  </span><br><span class="line">　　单引号更严格一些。它可以防止任何变量扩展。双引号可以防止通配符扩展但允许变量扩展。</span><br><span class="line">``` bash</span><br><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$SHELL</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="variable">$SHELL</span>"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">'$SHELL'</span></span><br></pre></td></tr></table></figure></li></ol><p>运行结果为：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/bin/bash</span><br><span class="line">/bin/bash</span><br><span class="line"><span class="variable">$SHELL</span></span><br></pre></td></tr></table></figure></p><p>　　最后，还有一种防止这种扩展的方法，那就是使用转义字符——反斜杆：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> *.jpg  </span><br><span class="line"><span class="built_in">echo</span> SHELL</span><br></pre></td></tr></table></figure></p><p>这将输出：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">∗.jpg SHELL</span><br></pre></td></tr></table></figure></p><ol start="6"><li>Here documents<br>当要将几行文字传递给一个命令时，here documents（译者注：目前还没有见到过对该词适合的翻译一种不错的方法。对每个脚本写一段帮助性的文字是很有用的，此时如果我们四有那个 here documents就不必用echo函数一行行输出。 一个 “Here document” 以 &lt;&lt; 开头，后面接上一个字符串，这个字符串还必须出现在here document的末尾。下面是一个例子，在该例子中，我们对多个文件进行重命名，并且使用here documents打印帮助：<pre><code class="bash"><span class="meta">#!/bin/sh</span><span class="comment"># we have less than 3 arguments. Print the help text:</span><span class="keyword">if</span> [ <span class="variable">$#</span> -lt 3 ] ; <span class="keyword">then</span>cat &lt;<span class="comment">#ren -- renames a number of files using sed regular expressions</span>USAGE: ren <span class="string">'regexp'</span> <span class="string">'replacement'</span> files...EXAMPLE: rename all *.HTM files <span class="keyword">in</span> *.html:　ren <span class="string">'HTM$'</span> <span class="string">'html'</span> *.HTMHELP　<span class="built_in">exit</span> 0<span class="keyword">fi</span>OLD=<span class="string">"<span class="variable">$1</span>"</span>NEW=<span class="string">"<span class="variable">$2</span>"</span><span class="comment"># The shift command removes one argument from the list of</span><span class="comment"># command line arguments.</span><span class="built_in">shift</span><span class="built_in">shift</span><span class="comment"># $* contains now all the files:</span><span class="keyword">for</span> file <span class="keyword">in</span> $*; <span class="keyword">do</span>　　<span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$file</span>"</span> ] ; <span class="keyword">then</span>　　newfile=`<span class="built_in">echo</span> <span class="string">"<span class="variable">$file</span>"</span> | sed <span class="string">"s/<span class="variable">${OLD}</span>/<span class="variable">${NEW}</span>/g"</span>`　　　<span class="keyword">if</span> [ -f <span class="string">"<span class="variable">$newfile</span>"</span> ]; <span class="keyword">then</span>　　　　<span class="built_in">echo</span> <span class="string">"ERROR: <span class="variable">$newfile</span> exists already"</span>　　　<span class="keyword">else</span>　　　　<span class="built_in">echo</span> <span class="string">"renaming <span class="variable">$file</span> to <span class="variable">$newfile</span> ..."</span>　　　　mv <span class="string">"<span class="variable">$file</span>"</span> <span class="string">"<span class="variable">$newfile</span>"</span>　　　<span class="keyword">fi</span>　　<span class="keyword">fi</span><span class="keyword">done</span></code></pre>　这是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是否小于3个 (特殊变量# 表示包含参数的个数) 。如果输入参数小于3个，则将帮助文字传递给cat命令，然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。 如果输入参数等于或大于3个，我们就将第一个参数赋值给变量OLD，第二个参数赋值给变量NEW。下一步，我们使用shift命令将第一个和第二个参数从 参数列表中删除，这样原来的第三个参数就成为参数列表# 表示包含参数的个数) 。如果输入参数小于3个，则将帮助文字传递 给cat命令，然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。 如果输入参数等 于或大于3个，我们就将第一个参数赋值给变量OLD，第二个参数赋值给变量NEW。下一步，我 们使用shift命令将第一个和第二个参数从 参数列表中删除，这样原来的第三个参数就成为参 数列表*的第一个参数。然后我们开始循环，命令行参数列表被一个接一个地被赋值给变量$file。  接着我 们判断该文件是否存在，如果存在则通过sed命令搜索和替换来产生新的文件名。然后  将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目 的：得到了旧文件名和新  文件名。然后使用mv命令进行重命名。</li></ol><h3 id="4-函数"><a href="#4-函数" class="headerlink" title="4)函数"></a>4)函数</h3><p>如果您写了一些稍微复杂一些的程序，您就会发现在程序中可能在几个地方使用了相同的代码，<br>并且您也会发现，如果我们使用了函数，会方便很多。一个函数是这个样子的：<br>functionname()<br>{</p><h1 id="inside-the-body-1-is-the-first-argument-given-to-the-function"><a href="#inside-the-body-1-is-the-first-argument-given-to-the-function" class="headerlink" title="inside the body $1 is the first argument given to the function"></a>inside the body $1 is the first argument given to the function</h1><h1 id="2-the-second-…"><a href="#2-the-second-…" class="headerlink" title="$2 the second …"></a>$2 the second …</h1><p>body<br>}<br>您需要在每个程序的开始对函数进行声明。<br>　　下面是一个叫做xtitlebar的脚本，使用这个脚本您可以改变终端窗口的名称。<br>这里使用了一个叫做help的函数。正如您可以看到的那样，这个定义的函数被使用了两次。</p><h1 id="bin-sh"><a href="#bin-sh" class="headerlink" title="!/bin/sh"></a>!/bin/sh</h1><h1 id="vim-set-sw-4-ts-4-et"><a href="#vim-set-sw-4-ts-4-et" class="headerlink" title="vim: set sw=4 ts=4 et:"></a>vim: set sw=4 ts=4 et:</h1><p>help()<br>{<br>　　cat &lt;<br>xtitlebar – change the name of an xterm, gnome-terminal or kde konsole<br>USAGE: xtitlebar [-h] “string_for_titelbar”<br>OPTIONS: -h help text<br>EXAMPLE: xtitlebar “cvs”<br>HELP<br>　　exit 0<br>}</p><h1 id="in-case-of-error-or-if-h-is-given-we-call-the-function-help"><a href="#in-case-of-error-or-if-h-is-given-we-call-the-function-help" class="headerlink" title="in case of error or if -h is given we call the function help:"></a>in case of error or if -h is given we call the function help:</h1><p>[ -z “1” ] &amp;&amp; help<br>[ “1” ] &amp;&amp; help [ “1” = “-h” ] &amp;&amp; help</p><h1 id="send-the-escape-sequence-to-change-the-xterm-titelbar"><a href="#send-the-escape-sequence-to-change-the-xterm-titelbar" class="headerlink" title="send the escape sequence to change the xterm titelbar:"></a>send the escape sequence to change the xterm titelbar:</h1><p>echo -e “33]0;107”     </p><h1 id=""><a href="#" class="headerlink" title=" "></a> </h1><p>在脚本中提供帮助是一种很好的编程习惯，这样方便其他用户（和您）使用和理解脚本。<br>命令行参数<br>　　我们已经见过107” # 在脚本中提供帮助是一种很好的编程习惯，这样方便其他用户（和您）使用和理解脚本。 命令行参数 　　我们已经见过* 和 1,1, 2 … $9 等特殊变量，这些特殊变量包含了用户从命令<br>行输入的参数。迄今为止，我们仅仅了解了一些简单的命令行语法（比如一些强制性的<br>参数和查看帮助的-h选项）。 但是在编写更复杂的程序时，您可能会发现您需要更多的<br>自定义的选项。通常的惯例是在所有可选的参数之前加一个减号，后面再加上参数值 (<br>比如文件名)。<br>有好多方法可以实现对输入参数的分析，但是下面的使用case表达式的例子无遗是一个不错的方法。</p><h1 id="bin-sh-1"><a href="#bin-sh-1" class="headerlink" title="!/bin/sh"></a>!/bin/sh</h1><p>help()<br>{<br>　cat &lt;<br>This is a generic command line parser demo.<br>USAGE EXAMPLE: cmdparser -l hello -f – -somefile1 somefile2<br>HELP<br>　exit 0<br>}<br>while [ -n “1”];docase1” ]; do case 1 in<br>　　-h) help;shift 1;; # function help is called<br>　　-f) opt_f=1;shift 1;; # variable opt_f is set<br>　　-l) opt_l=2;shift 2;; # -l takes an argument -&gt; shift by 2<br>　　–) shift;break;; # end of options<br>　　-<em>) echo “error: no such option2;shift 2;; # -l takes an argument -&gt; shift by 2 　　–) shift;break;; # end of options 　　-</em>) echo “error: no such option 1. -h for help”;exit 1;;<br>　　*) break;;<br>esac<br>done<br>echo “opt_f is optf”echo“optlisopt_f” echo “opt_l is opt_l”<br>echo “first arg is 1”echo“2ndargis1” echo “2nd arg is 2”<br>　　您可以这样运行该脚本：<br>cmdparser -l hello -f – -somefile1 somefile2<br>　　返回的结果是：<br>opt_f is 1<br>opt_l is hello<br>first arg is -somefile1<br>2nd arg is somefile2<br>　　这个脚本是如何工作的呢？脚本首先在所有输入命令行参数中进行循环，将输入参数<br>与case表达式进行比较，如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例，<br>首先输入的应该是包含减号的参数.<br>第2部分 实例<br>现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。并且写一个伪脚本（framework.sh），该脚本包含了大多数脚本都需要的框架结构，是一个非常不错的主意。这时候，在写一个新的脚本时我们只需要执行一下copy命令：<br>cp framework.sh myscript<br>　然后再插入自己的函数。<br>　　让我们再看两个例子：<br>　　二进制到十进制的转换<br>　　脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子：</p><h1 id="bin-sh-2"><a href="#bin-sh-2" class="headerlink" title="!/bin/sh"></a>!/bin/sh</h1><h1 id="vim-set-sw-4-ts-4-et-1"><a href="#vim-set-sw-4-ts-4-et-1" class="headerlink" title="vim: set sw=4 ts=4 et:"></a>vim: set sw=4 ts=4 et:</h1><p>help()<br>{<br>　cat &lt;<br>b2h – convert binary to decimal<br>USAGE: b2h [-h] binarynum<br>OPTIONS: -h help text<br>EXAMPLE: b2h 111010<br>will return 58<br>HELP<br>　exit 0<br>}<br>error()<br>{<br>　　# print an error and exit<br>　　echo “1”<br>　　exit 1<br>}<br>lastchar()<br>{<br>　　# return the last character of a string in1” 　　exit 1 } lastchar() { 　　# return the last character of a string in rval<br>　　if [ -z “$1” ]; then<br>　　　　# empty string<br>　　　　rval=””<br>　　　　return<br>　　fi<br>　　# wc puts some space behind the output this is why we need sed:<br>　　numofchar=<code>echo -n &quot;$1&quot; | wc -c | sed &#39;s/ //g&#39;</code><br>　　# now cut out the last char<br>　　rval=<code>echo -n &quot;$1&quot; | cut -b $numofchar</code><br>}<br>chop()<br>{<br>　　# remove the last character in string and return it in rvalif[−z“rval 　　if [ -z “1” ]; then<br>　　　　# empty string<br>　　　　rval=””<br>　　　　return<br>　　fi<br>　　# wc puts some space behind the output this is why we need sed:<br>　　numofchar=<code>echo -n &quot;$1&quot; | wc -c | sed &#39;s/ //g&#39;</code><br>　　if [ “$numofchar” = “1” ]; then<br>　　　　# only one char in string<br>　　　　rval=””<br>　　　　return<br>　　fi<br>　　numofcharminus1=<code>expr $numofchar &quot;-&quot; 1</code><br>　　# now cut all but the last char:<br>　　rval=<code>echo -n &quot;$1&quot; | cut -b 0-${numofcharminus1}</code><br>}<br>while [ -n “1”];docase1” ]; do case 1 in<br>　　-h) help;shift 1;; # function help is called<br>　　–) shift;break;; # end of options<br>　　-<em>) error “error: no such option $1. -h for help”;;  　　</em>) break;;<br>esac<br>done</p><h1 id="The-main-program"><a href="#The-main-program" class="headerlink" title="The main program"></a>The main program</h1><p>sum=0<br>weight=1</p><h1 id="one-arg-must-be-given"><a href="#one-arg-must-be-given" class="headerlink" title="one arg must be given:"></a>one arg must be given:</h1><p>[ -z “1” ] &amp;&amp; help<br>binnum=”1” ] &amp;&amp; help binnum=”1”<br>binnumorig=”1”while[−n“1” while [ -n “binnum” ]; do<br>　　lastchar “binnum”if[“binnum” 　　if [ “rval” = “1” ]; then<br>　　　　sum=<code>expr &quot;$weight&quot; &quot;+&quot; &quot;$sum&quot;</code><br>　　fi<br>　　# remove the last position in binnumchop“binnum 　　chop “binnum”<br>　　binnum=”$rval”<br>　　weight=<code>expr &quot;$weight&quot; &quot;*&quot; 2</code><br>done<br>echo “binary binnumorigisdecimalbinnumorig is decimal sum”<br>　该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..)，比如二进制”10”可<br>以这样转换成十进制：<br>0 <em> 1 + 1 </em> 2 = 2<br>　　为了得到单个的二进制数我们是用了lastchar 函数。该函数使用wc –c计算字符个数，<br>然后使用cut命令取出末尾一个字符。Chop函数的功能则是移除最后一个字符。<br>文件循环程序<br>　　或许您是想将所有发出的邮件保存到一个文件中的人们中的一员，但是在过了几个月<br>以后，这个文件可能会变得很大以至于使对该文件的访问速度变慢。下面的 脚本rotatefile<br>可以解决这个问题。这个脚本可以重命名邮件保存文件（假设为outmail）为outmail.1，<br>而对于outmail.1就变成了outmail.2 等等等等…</p><h1 id="bin-sh-3"><a href="#bin-sh-3" class="headerlink" title="!/bin/sh"></a>!/bin/sh</h1><h1 id="vim-set-sw-4-ts-4-et-2"><a href="#vim-set-sw-4-ts-4-et-2" class="headerlink" title="vim: set sw=4 ts=4 et:"></a>vim: set sw=4 ts=4 et:</h1><p>ver=”0.1”<br>help()<br>{<br>　　cat &lt;<br>rotatefile – rotate the file name<br>USAGE: rotatefile [-h] filename<br>OPTIONS: -h help text<br>EXAMPLE: rotatefile out<br>This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1<br>and create an empty out-file<br>The max number is 10<br>version ver<br>HELP<br>　　exit 0<br>}<br>error()<br>{<br>　　echo “$1”<br>　　exit 1<br>}<br>while [ -n “ver HELP 　　exit 0 } error() { 　　echo “$1” 　　exit 1 } while [ -n “1” ]; do<br>case 1in−h)help;shift1;;–)break;;−∗)echo“error:nosuchoption1 in 　　-h) help;shift 1;; 　　–) break;; 　　-<em>) echo “error: no such option 1. -h for help”;exit 1;;  　　</em>) break;;<br>esac<br>done</p><h1 id="input-check"><a href="#input-check" class="headerlink" title="input check:"></a>input check:</h1><p>if [ -z “1”];thenerror“ERROR:youmustspecifyafile,use−hforhelp”fifilen=”1” ] ; then error “ERROR: you must specify a file, use -h for help” fi filen=”1”</p><h1 id="rename-any-1-2-etc-file"><a href="#rename-any-1-2-etc-file" class="headerlink" title="rename any .1 , .2 etc file:"></a>rename any .1 , .2 etc file:</h1><p>for n in 9 8 7 6 5 4 3 2 1; do<br>　　if [ -f “filen.filen.n” ]; then<br>　　　　p=<code>expr $n + 1</code><br>　　　　echo “mv filen.filen.n filen.filen.p”<br>　　　　mv filen.filen.n filen.filen.p<br>　　fi<br>done</p><h1 id="rename-the-original-file"><a href="#rename-the-original-file" class="headerlink" title="rename the original file:"></a>rename the original file:</h1><p>if [ -f “filen”];thenecho“mvfilen” ]; then 　　echo “mv filen filen.1”mvfilen.1” 　　mv filen filen.1fiechotouchfilen.1 fi echo touch filen<br>touch $filen<br>　　这个脚本是如何工作的呢？在检测用户提供了一个文件名以后，我们进行一个9到1的循环。文件9被命名为10，文件8重命名为9等等。循环完成之后，我们将原始文件命名为文件1同时建立一个与原始文件同名的空文件。<br>调试<br>　　最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译，插入一个echo命令也不需要多少时间。<br>　　shell也有一个真实的调试模式。如果在脚本”strangescript” 中有错误，您可以这样来进行调试：<br>sh -x strangescript<br>　　这将执行该脚本并显示所有变量的值。<br>　　shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用：<br>sh -n your_script<br>　　这将返回所有语法错误</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://blog.csdn.net/xmyzlz/article/details/8593228" target="_blank" rel="noopener">Linux脚本编写基础</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;本文主要介绍shell脚本编写的基础语法，开头、注释、变量和 环境变量，虽然不涉及具体东西，但打好基础确是最为关键的。&lt;br&gt;本文是一篇在学习中的文章，还没学习完，排版也还有一些问题，待继续&lt;/p&gt;
&lt;h1 id=&quot;shell脚本编写初步介绍&quot;&gt;&lt;a href=&quot;#shel
      
    
    </summary>
    
      <category term="Linux 脚本" scheme="http://yoursite.com/categories/Linux-%E8%84%9A%E6%9C%AC/"/>
    
    
      <category term="Linux 脚本" scheme="http://yoursite.com/tags/Linux-%E8%84%9A%E6%9C%AC/"/>
    
  </entry>
  
  <entry>
    <title>Android 编译</title>
    <link href="http://yoursite.com/Android-Make/"/>
    <id>http://yoursite.com/Android-Make/</id>
    <published>2015-06-29T06:21:51.000Z</published>
    <updated>2018-07-13T03:59:09.283Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>本文的目的是讲述Android代码编译过程,以及编译过程中Android.mk,Android.bp等文件里面变量的作用分析</p><h1 id="Android编译过程分析"><a href="#Android编译过程分析" class="headerlink" title="Android编译过程分析"></a>Android编译过程分析</h1><p><a href="https://blog.csdn.net/lizekun2010/article/details/52598105" target="_blank" rel="noopener">Android7.0 编译系统流程分析</a><br><a href="https://blog.csdn.net/huangyabin001/article/details/36383031" target="_blank" rel="noopener">深入理解：Android 编译系统</a></p><h1 id="Android-mk"><a href="#Android-mk" class="headerlink" title="Android.mk"></a>Android.mk</h1><p>一个Android.mk file用来向编译系统描述你的源代码。具体来说：该文件是GNU Makefile的一小部分，会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块，你也可以在几个模块中使用同一个源代码文件。每个模块属下列类型之一：<br>1）APK程序，一般的Android程序，编译打包生成apk文件<br>2）JAVA库，java类库，编译打包生成jar文件<br>3) C\C++应用程序，可执行的C\C++应用程序<br>4）C\C++静态库，编译生成C\C++静态库，并打包成.a文件<br>5）C\C++共享库， 编译生成共享库（动态链接库），并打包成.so， 有且只有共享库才能被安装/复制到您的应用软件（APK）包中。<br>（1）先看一个简单的例子：一个简单的”hello world”，比如下面的文件：<br>sources/helloworld/helloworld.c<br>sources/helloworld/Android.mk<br>相应的Android.mk文件会像下面这样：<br>———- cut here ——————<br>[cpp] view plaincopyprint?<br>1. LOCAL_PATH := (callmy−dir)2.include(call my-dir) 2. include (CLEAR_VARS)<br>3. LOCAL_MODULE<br>4. := helloworld<br>5. LOCAL_SRC_FILES := helloworld.c<br>6. include (BUILDSHAREDLIBRARY)———cuthere——————我们来解释一下这几行代码：1，LOCALPATH:=(BUILD_SHARED_LIBRARY) ——— cut here —————— 我们来解释一下这几行代码： 1，LOCAL_PATH := (call my-dir) ，一个Android.mk file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中，宏函数‘my-dir’, 由编译系统提供，用于返回当前路径（即包含Android.mk file文件的目录）。<br>2，include (CLEARVARS)，CLEARVARS由编译系统提供（(可以在android安装目录下的/build/core/config.mk文件看到其定义，为CLEARVARS:=( CLEAR_VARS)，CLEAR_VARS由编译系统提供（(可以在 android 安装目录下的/build/core/config.mk 文件看到其定义，为 CLEAR_VARS:= (BUILD_SYSTEM)/clear_vars.mk)），指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量（例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等…),除LOCAL_PATH 。这是必要的，因为所有的编译控制文件都在同一个GNU MAKE执行环境中，所有的变量都是全局的。<br>3，LOCAL_MODULE := helloworld，LOCAL_MODULE变量必须定义，以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的，而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀，换句话说，一个被命名为’foo’的共享库模块，将会生成’libfoo.so’文件。<br>4，LOCAL_SRC_FILES := helloworld.c，LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意，你不用在这里列出头文件和包含文件，因为编译系统将会自动为你找出依赖型的文件；仅仅列出直接传递给编译器的源代码文件就好。<br>此处虽没用到其他常用的还有：<br>5，LOCAL_C_INCLUDES：可选变量，表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录。示例：LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := (LOCALPATH)/../foo6，TARGETARCH：目标CPU平台的名字；TARGETPLATFORM：Android.mk解析的时候，目标Android平台的名字；ARGETARCHABI：暂时只支持两个value，armeabi和armeabi−v7a7，LOCALSTATICLIBRARIES:表示该模块需要使用哪些静态库，以便在编译时进行链接。8，LOCALSHAREDLIBRARIES:表示模块在运行时要依赖的共享库（动态库），在链接时就需要，以便在生成文件时嵌入其相应的信息。9，LOCALLDLIBS:编译模块时要使用的附加的链接器选项。10，LOCALARMMODE:默认情况下，arm目标二进制会以thumb的形式生成(16位)，你可以通过设置这个变量为arm如果你希望你的module是以32位指令的形式11，LOCALCFLAGS:可选的编译器选项，在编译C代码文件的时候使用12，include(LOCAL_PATH)/../foo 6，TARGET_ARCH：目标 CPU平台的名字；TARGET_PLATFORM：Android.mk 解析的时候，目标 Android 平台的名字；ARGET_ARCH_ABI：暂时只支持两个 value，armeabi 和 armeabi-v7a 7，LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库，以便在编译时进行链接。 8，LOCAL_SHARED_LIBRARIES: 表示模块在运行时要依赖的共享库（动态库），在链接时就需要，以便在生成文件时嵌入其相应的信息。 9，LOCAL_LDLIBS: 编译模块时要使用的附加的链接器选项。 10，LOCAL_ARM_MODE: 默认情况下， arm目标二进制会以 thumb 的形式生成(16 位)，你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的形式 11，LOCAL_CFLAGS: 可选的编译器选项，在编译 C 代码文件的时候使用 12，include (call all-subdir-makefiles)：返回一个位于当前’my-dir’路径的子目录中的所有Android.mk的列表。<br>（2）在Android中增加本地程序或者库，这些程序和库与其所载路径没有任何关系，只和它们的Android.mk文件有关系。Android.mk和普通的Makefile有所不同，它具有统一的写法，主要包含一些系统公共的宏。在一个Android.mk中可以生成多个可执行程序、动态库和静态库。<br>A，编译C/C++应用程序的模板：  </p><p>#Test Exe<br>LOCAL_PATH := (call my-dir)    </p><pre><code>#include(call my-dir) #include (CLEAR_VARS)  </code></pre><p>LOCAL_SRC_FILES:= main.c<br>LOCAL_MODULE:= test_exe  </p><p>#LOCAL_C_INCLUDES :=  </p><p>#LOCAL_STATIC_LIBRARIES :=  </p><p>#LOCAL_SHARED_LIBRARIES :=<br>include (BUILDEXECUTABLE)（菜鸟级别解释：:=是赋值的意思，+=是追加的意思，(BUILD_EXECUTABLE) （菜鸟级别解释：:=是赋值的意思，+=是追加的意思，是引用某变量的值）BUILD_EXECUTABLE表示以一个可执行程序的方式进行编译。补充说明：include (BUILDPACKAGE)则是编译出一个apk，include(BUILD_PACKAGE)则是编译出一个apk，include (BUILD_STATIC_JAVA_LIBRARY)则是编译出jar包。<br>B，编译静态库的模板：  </p><p>#Test Static Lib<br>LOCAL_PATH := (callmy−dir)include(call my-dir) include (CLEAR_VARS)<br>LOCAL_SRC_FILES:= /<br>helloworld.c<br>LOCAL_MODULE:= libtest_static  </p><p>#LOCAL_C_INCLUDES :=  </p><p>#LOCAL_STATIC_LIBRARIES :=  </p><p>#LOCAL_SHARED_LIBRARIES :=<br>include (BUILD_STATIC_LIBRARY)<br>一般的和上面相似，BUILD_STATIC_LIBRARY表示编译一个静态库.a文件。静态库不会复制到的APK包中，但是能够用于编译共享库。<br>C，编译动态库的模板：    </p><pre><code>#Test Shared Lib    LOCAL_PATH :=(BUILD\_STATIC\_LIBRARY) 一般的和上面相似，BUILD\_STATIC\_LIBRARY表示编译一个静态库.a文件。静态库不会复制到的APK包中，但是能够用于编译共享库。 C，编译动态库的模板： #Test Shared Lib LOCAL_PATH := (call my-dir)  </code></pre><p>include (CLEAR_VARS)<br>     LOCAL_SRC_FILES:= /<br>               helloworld.c<br>     LOCAL_MODULE:= libtest_shared<br>     TARGET_PRELINK_MODULES := false    </p><pre><code>#LOCAL\_C\_INCLUDES :=    #LOCAL\_STATIC\_LIBRARIES :=    #LOCAL\_SHARED\_LIBRARIES :=     include(CLEAR\_VARS) LOCAL\_SRC\_FILES:= / helloworld.c LOCAL\_MODULE:= libtest\_shared TARGET\_PRELINK\_MODULES := false #LOCAL\_C\_INCLUDES := #LOCAL\_STATIC\_LIBRARIES := #LOCAL\_SHARED_LIBRARIES := include (BUILD\_SHARED\_LIBRARY)  </code></pre><p>一般的和上面相似，BUILD_SHARED_LIBRARY表示编译一个动态库。<br>以上三者的生成结果分别在如下，generic依具体target会变：<br>out/target/product/generic/obj/EXECUTABLE<br>out/target/product/generic/obj/STATIC_LIBRARY<br>out/target/product/generic/obj/SHARED_LIBRARY<br>每个模块的目标文件夹分别为：<br>可执行程序：XXX_intermediates<br>静态库： XXX_static_intermediates<br>动态库： XXX_shared_intermediates<br>另外，在Android.mk文件中，还可以指定最后的目标安装路径，用LOCAL_MODULE_PATH和LOCAL_UNSTRIPPED_PATH来指定。不同的文件系统路径用以下的宏进行选择：<br>TARGET_ROOT_OUT：表示根文件系统out/target/product/generic/root。<br>TARGET_OUT：表示system文件系统out/target/product/generic/system。<br>TARGET_OUT_DATA：表示data文件系统out/target/product/generic/data。<br>OUT_DIR：代码工程编译时的out生成目录<br>PRODUCT_OUT：映象生成目录</p><h1 id="Android-bp"><a href="#Android-bp" class="headerlink" title="Android.bp"></a>Android.bp</h1><p> 从Android 7.0 (N)开始, Google开始逐步使用Android.bp代替原来的Android.mk进行编译.<br> Google称之为soong, 具体可以参考:<br> <a href="https://android.googlesource.com/platform/build/soong" target="_blank" rel="noopener">https://android.googlesource.com/platform/build/soong</a><br> 使用Android.bp编译时, 目前还存在一些问题:<br> 1.对C/C++代码, 如果需要使用宏开关时, 由于android整个编译系统还没完全切换过来, 导致 在项目mk文件定义的开关, 还不能生效.<br> 2.对于条件编译, 需要添加go文件进行控制.</p><p> 对于问题1, 一是通过export命令, 把相应的开关设置到环境变量, go文件就能读取到了.<br> 二是, 把开关集中放到某一个文件中, 然后在go文件中直接读取这个文件.<br> 对于问题2, 下面会通过例子给出一个说明.<br> 下面是一个libsysutils模块Android.bp的内容: libsysutils_defaults以上及相关内容是新添加关键部分, 用来支持go文件进行条件编译. 如果想让TLV_FEATURE_ENABLED起作用, 需要export TLV_FEATURE_ENABLED=yes.</p><p>待补充</p><p><a href="https://blog.csdn.net/drageon_j/article/details/77336817" target="_blank" rel="noopener">Android 编译系统之Android.bp</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;本文的目的是讲述Android代码编译过程,以及编译过程中Android.mk,Android.bp等文件里面变量的作用分析&lt;/p&gt;
&lt;h1
      
    
    </summary>
    
      <category term="Android 编译" scheme="http://yoursite.com/categories/Android-%E7%BC%96%E8%AF%91/"/>
    
    
      <category term="make" scheme="http://yoursite.com/tags/make/"/>
    
  </entry>
  
  <entry>
    <title>问题锦集-速查</title>
    <link href="http://yoursite.com/%E9%97%AE%E9%A2%98%E9%94%A6%E9%9B%86-%E9%80%9F%E6%9F%A5/"/>
    <id>http://yoursite.com/问题锦集-速查/</id>
    <published>2015-06-29T06:17:50.000Z</published>
    <updated>2018-08-20T02:04:09.626Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>本文收集平常工作过程中遇到的一些常见问题，遇到的一些坑，把它们记录下来，以便日后再次遇到能快速排查。</p><h1 id="无法下载版本问题："><a href="#无法下载版本问题：" class="headerlink" title="无法下载版本问题："></a>无法下载版本问题：</h1><ol><li><p>确认主板中是否有download过软件——直接接电源开机，如可以开机，可以断定此主板已经down过版本了；如果无法开机，尝试焊接串口线，抓一下log，看看在哪一步停下来走不下去了。<br>一般而言，可以开机但是不能下载的，存在两种可能：<br>①新软件中未兼容主板上的Memory物料；<br>②分区表更新了。<br>对于这第一种情况，可以通过查配置表，找到Memory物料型号，然后跟软件开发人员核对，是否有兼容；或者查看flash download工具的log，发给开发人员，通过Memory ID来确认是否兼容该物料。<br>对于第二种情况，通常可以选择upgrade的升级方式，如实在不行，可以导出校准参数，然后格式化下载新软件，最后重新导入校准参数。 </p></li><li><p>如果遇到无法开机，又无法下载的，可能有三种种情况：<br>①软件中未兼容主板上的Memory物料；<br>②下载工具版本过旧，不支持当前平台的下载；<br>③主板有异常，尤其Memory可能有损坏。<br>第一种情况跟上面类似，不再赘述。第二种情况，可以先直接换一个新版本工具试试能否下载，如可以下载，说明工具需要更新，如仍然无法下载，说明跟工具无关。第三种情况，可以多找几块主板，如果有一块可以下载软件，基本就可以判断无法下载的主板存在硬件上的损坏。  </p></li><li><p>不开机问题：<br>1、 按开机键没有反应，屏幕不亮，用电源连接时发现电流无变化。这种情况，有两种可能：<br>①主板开机键或电池连接异常；<br>②单块主板损坏；<br>第一种情况，可以直接使用电源，然后用镊子连接主板power key测试点与地，如可以开机，说明开机键或电池异常；<br>第二种情况通常需要硬件同事介入分析。<br>2、开机到某个阶段停止不动。类似这样的情况，可以直接抓log分析。<br>比较实用的方法：①抓取不开机的uart log，部分手机不开机，同时抓取正常开机的uart log对比分析；②如果开机到android界面，可使用adb logcat -v time &gt; log.txt获取上层log信息。</p></li><li><p>部分手机开机时间非常长：<br>此类问题很大可能性是由于外设在读ID导致的，直接抓log查看即可。最方便的方式就是直接抓串口log来分析。此外还可以用adb shell logcat –v time &gt; 123.txt来抓取上层log来分析。</p></li><li><p>开机背光亮，有开机铃声，屏幕没有显示：<br>一般而言，问题在于屏没有兼容或者屏/主板单体损坏。如果是单体问题，我们可以做交叉实验，分析问题是主板还是屏导致的。如果全部显示都不对，可以跟软件开发同事确认是否软件不对，或者抓串口log，确认屏ID是否读到。</p></li><li><p>Camera应用丢失，Camera切换按钮丢失：<br>在MTK平台上，Camera的识别是在开机过程中完成的。如果开机时没有识别到任何Camera，开机之后Camera应用就会直接影藏；如果只识别到一个Camera，Camera切换按钮就是影藏。<br>1、如果有发现手机开机之后，Camera应用丢失，可以通过以下步骤去：<br>①将手机恢复出厂设置，然后重新开机，此时如果Camera应用显示出来，且多次使用都正常了，说明之前该机器很有可能是先开机过，然后再安装Camera的。<br>②如果恢复出厂设置还是无法显示Camera应用，首先需要确认此问题是单体（或小概率）问题还是大批量问题。单体问题的话，就需要去排查Camera焊接/连接是否正常。如焊接/连接正常，接着做交叉实验，确认是模组问题还是主板问题。模组问题就安排模组厂分析，主板问题就合硬件沟通解决。如果是大批量问题，一方面需要让模组厂参与进来分析，另一方面需要排查软件/硬件。软件上可以抓串口log给软件同事分析，硬件上排查走线、供电是否有异常。<br>③如果切换异常，通常是某个Camera的ID没有读到。可以在拨号界面输入“_#<em>#9375#</em>#_”（仅适用于MTK平台），确认下是否是没有读到ID，如确实ID没有读到，一方面检查Camera焊接/连接是否正常，另一方面抓下串口log给软件同事分析。</p></li><li><p>Camera花屏/闪绿线：<br>遇到Camera使用过程中花屏/闪绿线的问题，我们可以从两个方向去排查问题。首先还是按照惯例统计花屏的概率，如果是单体问题，需要去确认Camera数据线是否有问题，直接测量数据线信号即可，将波形发生给硬件同事检查。然后做交叉实验，确认是模组问题还是主板问题。模组问题让模组厂来分析，主板问题可以跟硬件沟通处理。<br>如果大批量都有问题，需要软件和硬件同事介入分析。软件上尝试修改Camera驱动能力，优化一个最好的组合，硬件上尝试包导电布、做接地处理。</p></li></ol><h1 id="技巧-Android-打印堆栈"><a href="#技巧-Android-打印堆栈" class="headerlink" title="技巧 Android 打印堆栈"></a>技巧 Android 打印堆栈</h1><p>Java 方法打印堆栈<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">RuntimeException here = new RuntimeException(<span class="string">"here"</span>);</span><br><span class="line">       here.fillInStackTrace();</span><br><span class="line">       Log.w(<span class="string">"eric"</span>, <span class="string">"Called: "</span> , here);</span><br><span class="line">       //here.printStackTrace();</span><br></pre></td></tr></table></figure></p><p>如在PanelView的onTouchEvent方法中打log后，调用堆栈如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">01-03 17:53:39.860  8861  8861 W eric    : Called: </span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : java.lang.RuntimeException: here</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at com.android.systemui.statusbar.phone.PanelView.onTouchEvent(PanelView.java:265)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at com.android.systemui.statusbar.phone.NotificationPanelView.onTouchEvent(NotificationPanelView.java:860)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.View.dispatchTouchEvent(View.java:11781)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2962)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2643)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2968)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at com.android.systemui.statusbar.phone.StatusBarWindowView.dispatchTouchEvent(StatusBarWindowView.java:290)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.View.dispatchPointerEvent(View.java:12020)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$ViewPostImeInputStage</span>.processPointerEvent(ViewRootImpl.java:4809)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$ViewPostImeInputStage</span>.onProcess(ViewRootImpl.java:4623)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.deliver(ViewRootImpl.java:4161)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.onDeliverToNext(ViewRootImpl.java:4214)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.forward(ViewRootImpl.java:4180)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$AsyncInputStage</span>.forward(ViewRootImpl.java:4307)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.apply(ViewRootImpl.java:4188)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$AsyncInputStage</span>.apply(ViewRootImpl.java:4364)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.deliver(ViewRootImpl.java:4161)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.onDeliverToNext(ViewRootImpl.java:4214)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.forward(ViewRootImpl.java:4180)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.apply(ViewRootImpl.java:4188)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$InputStage</span>.deliver(ViewRootImpl.java:4161)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6682)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6656)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6617)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$WindowInputEventReceiver</span>.onInputEvent(ViewRootImpl.java:6785)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:186)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:177)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6756)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.ViewRootImpl<span class="variable">$ConsumeBatchedInputRunnable</span>.run(ViewRootImpl.java:6808)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.Choreographer<span class="variable">$CallbackRecord</span>.run(Choreographer.java:911)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.Choreographer.doCallbacks(Choreographer.java:723)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.Choreographer.doFrame(Choreographer.java:652)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.view.Choreographer<span class="variable">$FrameDisplayEventReceiver</span>.run(Choreographer.java:897)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.os.Handler.handleCallback(Handler.java:790)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.os.Handler.dispatchMessage(Handler.java:99)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.os.Looper.loop(Looper.java:164)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at android.app.ActivityThread.main(ActivityThread.java:6501)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at java.lang.reflect.Method.invoke(Native Method)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at com.android.internal.os.RuntimeInit<span class="variable">$MethodAndArgsCaller</span>.run(RuntimeInit.java:438)</span><br><span class="line">01-03 17:53:39.860  8861  8861 W eric    : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)</span><br><span class="line">01-03 17:53:39.877  8861  8861 I chatty  : uid=10034(com.android.systemui) identical 1 line</span><br></pre></td></tr></table></figure></p><h1 id="查看View属性的值"><a href="#查看View属性的值" class="headerlink" title="查看View属性的值"></a>查看View属性的值</h1><p><a href="https://blog.csdn.net/xingchenxuanfeng/article/details/54629406" target="_blank" rel="noopener">发现安卓里一个牛逼的开发工具ViewDebug.ExportedProperty</a></p><h1 id="Android-中Trace-log"><a href="#Android-中Trace-log" class="headerlink" title="Android 中Trace log"></a>Android 中Trace log</h1><p> 在Android中经常看到Trace.beginSection之类的代码如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) &#123;</span><br><span class="line">    Trace.beginSection(<span class="string">"KeyguardViewMediator#startKeyguardExitAnimation"</span>);</span><br><span class="line">    Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,</span><br><span class="line">            new StartKeyguardExitAnimParams(startTime, fadeoutDuration));</span><br><span class="line">    mHandler.sendMessage(msg);</span><br><span class="line">    Trace.endSection();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://blog.sina.com.cn/s/blog_769500f001019fy5.html" target="_blank" rel="noopener">Android下打印调试堆栈方法</a><br><a href="https://blog.csdn.net/u013270444/article/details/53012976" target="_blank" rel="noopener">https://blog.csdn.net/u013270444/article/details/53012976</a><br><a href="https://blog.csdn.net/xingchenxuanfeng/article/details/54629406" target="_blank" rel="noopener">发现安卓里一个牛逼的开发工具ViewDebug.ExportedProperty</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;本文收集平常工作过程中遇到的一些常见问题，遇到的一些坑，把它们记录下来，以便日后再次遇到能快速排查。&lt;/p&gt;
&lt;h1 id=&quot;无法下载版本问
      
    
    </summary>
    
      <category term="问题" scheme="http://yoursite.com/categories/%E9%97%AE%E9%A2%98/"/>
    
    
      <category term="问题 锦集 速查" scheme="http://yoursite.com/tags/%E9%97%AE%E9%A2%98-%E9%94%A6%E9%9B%86-%E9%80%9F%E6%9F%A5/"/>
    
  </entry>
  
  <entry>
    <title>各类工具使用小技巧</title>
    <link href="http://yoursite.com/%E5%90%84%E7%B1%BB%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E5%B0%8F%E6%8A%80%E5%B7%A7/"/>
    <id>http://yoursite.com/各类工具使用小技巧/</id>
    <published>2015-06-29T06:06:59.000Z</published>
    <updated>2018-09-07T11:16:48.207Z</updated>
    
    <content type="html"><![CDATA[<h1 id="VIM-使用小技巧"><a href="#VIM-使用小技巧" class="headerlink" title="VIM 使用小技巧"></a>VIM 使用小技巧</h1><h2 id="配置鼠标和显示行号"><a href="#配置鼠标和显示行号" class="headerlink" title="配置鼠标和显示行号"></a>配置鼠标和显示行号</h2><p>服务器在安装了加密之后，vim无法使用鼠标，也无法显示行号了，可以通过如下方法修改：  </p><ol><li>在~路径下创建.vimrc，然后加入两行<br>set mouse=a<br>set nu<br>修改完之后保存退出。  </li><li>关闭bash窗口，或者source .vimrc，接下来vim就可以使用鼠标和显示行号了。</li></ol><h1 id="shell-脚本小技巧"><a href="#shell-脚本小技巧" class="headerlink" title="shell 脚本小技巧"></a>shell 脚本小技巧</h1><h2 id="shell脚本注意事项"><a href="#shell脚本注意事项" class="headerlink" title="shell脚本注意事项"></a>shell脚本注意事项</h2><p>有些情况下，我们会碰到一个问题，在编译时，有些shell脚本会无法执行。相信大家会第一时间去检查脚本的权限，看看脚本是否有可执行权限——ls查看下。如果权限不对，使用chmod指令修改下就好了。<br>但是有些时候，发现权限对了，脚本执行仍然不对，如这种提示——bad interpreter:No such file or directory。这种情况，其实是脚本的文件结尾符是dos格式的，而非unix标准编码。<br>碰到这种情况，可以通过如下方式定位、解决。  </p><ol><li>使用vim打开文件，然后进入命令模式，输入set: ff（ ）并回车，此时，文件下方会有提示 或者 如果是第二种，那么恭喜你中招了，这个文件的格式是dos的（也就是windows一系列系统的格式），这种格式的脚本，是无法在shell中执行的。  </li><li>想要修改，也非常简单。输入命令 ，然后回车，问题搞定了。保存文件，会发现该脚本已经可以执行了。</li></ol><p>Linux中，更改某个目录的权限<br>sudo chmod -R 777 /honme/zzz</p><h1 id="Putty-使用小技巧"><a href="#Putty-使用小技巧" class="headerlink" title="Putty 使用小技巧"></a>Putty 使用小技巧</h1><h2 id="实现输入中文和log行数增加"><a href="#实现输入中文和log行数增加" class="headerlink" title="实现输入中文和log行数增加"></a>实现输入中文和log行数增加</h2><ol><li>在Translation标签页，将putty的字符集修改为UTF-8。  </li><li>将字体设置成Fixedsys。  </li><li>在Window标签页，将Lines of Scrollback设置成为20000。  </li><li>将Session中的IP地址和Saved Session填写好。选择Save。  </li><li>下次登录的时候，只要选择存储的Session就可以了。</li></ol><h1 id="Hexo-Markdown-插入图片"><a href="#Hexo-Markdown-插入图片" class="headerlink" title="Hexo-Markdown 插入图片"></a>Hexo-Markdown 插入图片</h1><p><a href="https://blog.csdn.net/qq_26891045/article/details/51693571" target="_blank" rel="noopener">参考</a><br><a href="https://www.jianshu.com/p/cf0628478a4e" target="_blank" rel="noopener">这种方法加载失败</a><br>实例：<br><img src="/各类工具使用小技巧/Android-Window-mechanism/window-01.png" alt="&quot;Window的界面解析&quot;"></p><p>Android-PackageManagerService-Analysis/4-packagemanager-packageparser-parcelable.png</p><h1 id="Hexo-站内文章之间跳转"><a href="#Hexo-站内文章之间跳转" class="headerlink" title="Hexo 站内文章之间跳转"></a>Hexo 站内文章之间跳转</h1><p> 可以从当前文章，跳转到另外的一篇文章，实例见Android中Window机制 这篇文章<br> 首先要分析Window的创建过程，就必须了解<a href="../Android-Launcher-Activity">Activity的启动过程</a>。 </p><h1 id="MarkDown-实现业内跳转"><a href="#MarkDown-实现业内跳转" class="headerlink" title="MarkDown 实现业内跳转"></a>MarkDown 实现业内跳转</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1. 先定义一个锚(id)</span><br><span class="line">    &lt;span id=<span class="string">"jump"</span>&gt;Hello World&lt;/span&gt;</span><br><span class="line">2. 然后使用markdown的语法:</span><br><span class="line">      [XXXX](<span class="comment">#jump)</span></span><br></pre></td></tr></table></figure><p>3.<a href="https://blog.csdn.net/github_32886825/article/details/52930195" target="_blank" rel="noopener">使用Sublime Text 3进行Markdown 编辑+实时预览</a></p><h1 id="Sublime-Text"><a href="#Sublime-Text" class="headerlink" title="Sublime Text"></a>Sublime Text</h1><h1 id="Android中的源码查看方式"><a href="#Android中的源码查看方式" class="headerlink" title="Android中的源码查看方式"></a>Android中的源码查看方式</h1><p>1) 通过SDK 可以查看framework/base/core下的Java和res源码<br>2) 通过Google官网 <a href="https://android.googlesource.com" target="_blank" rel="noopener">https://android.googlesource.com</a><br>3) Qualcommon网站 <a href="https://source.codeaurora.org" target="_blank" rel="noopener">https://source.codeaurora.org</a><br>4）OpenGork-1  <a href="http://android.macpod.net/" target="_blank" rel="noopener">http://android.macpod.net/</a><br>5) OpenGork-2 <a href="https://www.androidos.net.cn/" target="_blank" rel="noopener">https://www.androidos.net.cn/</a><br>6) OpenGork-3 <a href="http://androidxref.com/" target="_blank" rel="noopener">http://androidxref.com/</a></p><h1 id="Chrome-实用插件"><a href="#Chrome-实用插件" class="headerlink" title="Chrome 实用插件"></a>Chrome 实用插件</h1><p>OneTab<br>谷歌访问助手<br>tampermonkey<br>Github三件套： Octotree<br>Listen 1  <a href="http://listen1.github.io/listen1/" target="_blank" rel="noopener">http://listen1.github.io/listen1/</a><br>office online<br>JSON Viewer<br>JSON Editor<br>MarkDown Here<br>掘金<br>inoreader  RSS工具<br>印象笔记<br>书签侧边栏<br>privatix 代理</p><p><a href="../Chrome">Chrome插件开发</a></p><h1 id="图标素材"><a href="#图标素材" class="headerlink" title="图标素材"></a>图标素材</h1><p>Flatlcon<br><a href="https://flaticons.net/" target="_blank" rel="noopener">https://flaticons.net/</a><br><a href="https://www.flaticon.com/most-downloaded" target="_blank" rel="noopener">https://www.flaticon.com/most-downloaded</a><br><a href="https://pattern.flaticon.com/" target="_blank" rel="noopener">https://pattern.flaticon.com/</a></p><p>Easyicon</p><p>Fontello</p><p>Swifticons</p><p>Noun Project</p><h1 id="Android-反编译"><a href="#Android-反编译" class="headerlink" title="Android 反编译"></a>Android 反编译</h1><p><a href="https://blog.csdn.net/s13383754499/article/details/78914592" target="_blank" rel="noopener">https://blog.csdn.net/s13383754499/article/details/78914592</a></p><p>java -jar apktool_2.3.3.jar d -f YouTube.apk -o MMTS</p><h1 id="Git-使用"><a href="#Git-使用" class="headerlink" title="Git 使用"></a>Git 使用</h1><p><a href="https://juejin.im/entry/5b43ebb1f265da0fa509fd0f" target="_blank" rel="noopener">git clean还你一片清洁之地</a><br><a href="https://mp.weixin.qq.com/s/pQfxMGU6mB3AmZIiFpw2bQ" target="_blank" rel="noopener">一些你可能会用到的Git实用技巧和命令</a></p><ol><li>将某笔提交生成一个patch<br>最近的一次提交生成一个patch<br>git format-patch -1<br>git format-patch -s 1bbe3c8c197a35f79bfddaba099270a2e54ea9c7<br>git format-patch HEAD^ &lt;==最近的1次commit的patch<br>git format-patch HEAD^^ &lt;==最近的2次commit的patch<br>git format-patch HEAD^ &lt;==最近的3次commit的patch<br>git format-patch HEAD^ &lt;==最近的4次commit的patch<br>git format-patch HEAD^^^^^ &lt;==不支持！！！！error！！！</li></ol><h1 id="github"><a href="#github" class="headerlink" title="github"></a>github</h1><ol><li>github trend 总结了每天、每周、每日的热门项目和开发者</li><li>github topic 展示了最新和最流行的讨论主题，还能看到更多的非开发技术的讨论主题</li></ol><h1 id="RSS的使用"><a href="#RSS的使用" class="headerlink" title="RSS的使用"></a>RSS的使用</h1><p><a href="https://juejin.im/post/5af4042e6fb9a07ac23ac3ab" target="_blank" rel="noopener">RSS推送技术–打造自己的今日头条</a><br>Q: 希望通过用RSS查看关注网站之前的文章，如何操作？</p><h1 id="建站"><a href="#建站" class="headerlink" title="建站"></a>建站</h1><p><a href="https://www.cnblogs.com/wu-yun-jiang/p/4518204.html" target="_blank" rel="noopener">搭建自己的服务器和域名</a></p><h1 id="repo的使用"><a href="#repo的使用" class="headerlink" title="repo的使用"></a>repo的使用</h1><ol><li>对于某个项目，只是需要修改某个模块的代码，比如Launcher，可以只需要拉取该模块的代码<br>repo init 8909-la301<br>repo sync -c packages/app/Launcher3<br>这样就可以只拉取Launcher3模块的代码.<br>问题： 代码提交的时候，需要从本地分支提交代码。<br>新建本地分支的方法：<br>a) 之前的repo start master –all肯定不能使用<br>repo start master packages/apps/Launcher3<br>b) git checkout的方式</li></ol><p>常用图标：<br>└──<br>•<br>★</p><h1 id="小程序入坑问题锦集"><a href="#小程序入坑问题锦集" class="headerlink" title="小程序入坑问题锦集"></a>小程序入坑问题锦集</h1><ol><li>如何实现将需要展示的图片放到网站上<br>小程序可以直接到网站上获取图片等资源<br><a href="https://www.cnblogs.com/zhoubingyan/p/8979409.html" target="_blank" rel="noopener">https://www.cnblogs.com/zhoubingyan/p/8979409.html</a></li></ol><ol start="2"><li>为何同样大小的图片，就不能显示？</li></ol><h1 id="VirtualBox-安装Mac-OS"><a href="#VirtualBox-安装Mac-OS" class="headerlink" title="VirtualBox 安装Mac OS"></a>VirtualBox 安装Mac OS</h1><p><a href="https://blog.csdn.net/u012150264/article/details/51434550" target="_blank" rel="noopener">VirtualBOX安装Mac OS X 10.7正式版</a><br><a href="http://bbs.zol.com.cn/nbbbs/d160_148114.html" target="_blank" rel="noopener">全民吃苹果，首发VirtualBOX安装Mac OS X 10.7正式版_可完美升级，无bug</a></p><p><a href="https://www.linuxidc.com/Linux/2014-09/106558.htm" target="_blank" rel="noopener">Ubuntu下使用VirtualBox安装Mac OS X Mavericks(10.9)</a></p><ol><li>下载dmg格式文件<br><a href="http://www.pc6.com/mac/148040.html" target="_blank" rel="noopener">http://www.pc6.com/mac/148040.html</a></li><li>使用ultraiso工具将dmp文件转换成ISO格式</li><li>遇到问题：fatal:no bootable medium found! system halted!<br>目前暂未解决</li></ol><h1 id="WordPress"><a href="#WordPress" class="headerlink" title="WordPress"></a>WordPress</h1><p>弹性Web托管</p><h1 id="adb"><a href="#adb" class="headerlink" title="adb"></a>adb</h1><p>过滤某个TAG的log： adb logcat -s TAG</p><h1 id="Ubuntu-快捷键"><a href="#Ubuntu-快捷键" class="headerlink" title="Ubuntu 快捷键"></a>Ubuntu 快捷键</h1><p>Ctrl + Alt + F12 关闭图形界面， 显示器会黑掉， 没有任何的显示，但是显示器的指示灯是亮的<br>Ctrl + Alt + F7 开启图形界面，屏幕会亮起来.</p><h1 id="迅雷"><a href="#迅雷" class="headerlink" title="迅雷"></a>迅雷</h1><p>去掉迅雷中嵌入式字幕中的中文字幕<br>使用会声会影视频编缉软件，在字幕的位置加一个遮罩，渲染输出。</p><h1 id="跟踪函数调用"><a href="#跟踪函数调用" class="headerlink" title="跟踪函数调用"></a>跟踪函数调用</h1><ol><li>异常时打印函数调用信息<br>fillInStackTrace();//会清空之前的栈内信息<br>e.printStackTrace();</li><li></li></ol><h1 id="Android-mk-中过滤"><a href="#Android-mk-中过滤" class="headerlink" title="Android.mk 中过滤"></a>Android.mk 中过滤</h1><p>ifeq (, $(filter msm8953_x57_l8, $(TARGET_PRODUCT)))<br>…<br>endif</p><h1 id="在线编译运行小工具"><a href="#在线编译运行小工具" class="headerlink" title="在线编译运行小工具"></a>在线编译运行小工具</h1><p>适用于Php,Java,C,C++,Go等多种语言<br><a href="https://c.runoob.com/compile" target="_blank" rel="noopener">https://c.runoob.com/compile</a> 背景为白色<br><a href="https://tool.lu/coderunner/" target="_blank" rel="noopener">https://tool.lu/coderunner/</a>  背景为黑色</p><h1 id="ubuntu-如何统计代码行数"><a href="#ubuntu-如何统计代码行数" class="headerlink" title="ubuntu 如何统计代码行数"></a>ubuntu 如何统计代码行数</h1><p><a href="https://blog.csdn.net/xiao_yuanjl/article/details/78905160" target="_blank" rel="noopener">https://blog.csdn.net/xiao_yuanjl/article/details/78905160</a><br>方法一：</p><p>方法二：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//安装</span><br><span class="line">$ sudo apt-get install cloc</span><br><span class="line">//进入到需要统计的目录执行,注意后面有个“.“ 表示当前目录</span><br><span class="line">$ cloc .</span><br></pre></td></tr></table></figure></p><p>结果解释：<br>files 文件个数<br>blank 空白行数<br>comment 注释行数<br>code 代码行数</p><h1 id="9矢量图查看"><a href="#9矢量图查看" class="headerlink" title=".9矢量图查看"></a>.9矢量图查看</h1><p>Method Draw  <a href="http://editor.method.ac/" target="_blank" rel="noopener">http://editor.method.ac/</a><br>可以在此网站查看.9 patch路径画出来的图的样子.</p><h1 id="Understand-高效代码静态分析神器"><a href="#Understand-高效代码静态分析神器" class="headerlink" title="Understand 高效代码静态分析神器"></a>Understand 高效代码静态分析神器</h1><p><a href="http://codemx.cn/2016/04/30/Understand01/#" target="_blank" rel="noopener">Understand:高效代码静态分析神器详解(一)</a><br><a href="https://www.cnblogs.com/zhangyang/p/7602485.html" target="_blank" rel="noopener">Understand:高效代码静态分析神器详解(一)</a><br><a href="https://blog.csdn.net/u011776903/article/details/73563957/" target="_blank" rel="noopener">understand软件使用教程</a><br>该软件可以帮助你快速的分析代码结构流程.<br><a href="https://scitools.com/features/" target="_blank" rel="noopener">官网</a>官网下载收费，支持Linux、Windows、MAC系统.<br><a href="https://blog.csdn.net/look85/article/details/7988332" target="_blank" rel="noopener">Scientific Toolworks Understand for linux 的下载</a><br>Linux64位下载地址：<a href="https://pan.baidu.com/s/1i52nrut" target="_blank" rel="noopener">https://pan.baidu.com/s/1i52nrut</a><br>小技巧 ：<br>1、设置字体和颜色风格<br>修改默认字体：Tools - Options - Editor - Default style<br>修改颜色： Tools - Options - Editor - Styles<br>2、生成UML类图、调用树图<br>默认安装的插件不支持这两种图，需要从官网下载插件。<br>_<a href="http://www.scitools.com/perl_scripts/uperl/uml_class.upl" target="_blank" rel="noopener">http://www.scitools.com/perl_scripts/uperl/uml_class.upl</a><br>_<a href="http://www.scitools.com/perl_scripts/uperl/invocation.upl" target="_blank" rel="noopener">http://www.scitools.com/perl_scripts/uperl/invocation.upl</a><br>放到sti/conf/scripts/local目录下。<br>然后重新运行，执行 project- project graphical views - xxxx可以生成这两种图。<br>3、更改图的字体<br>直接修改对应的脚本文件（\Program Files\STI\conf\scripts目录下），在do_load( )函数的对应位置加入如下的设置：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$graph</span>- default( fontname , Consolas , node );</span><br><span class="line"><span class="variable">$graph</span>- default( fontsize , 10 , node );</span><br><span class="line"><span class="variable">$graph</span>- default( fontname , Consolas , edge );</span><br><span class="line"><span class="variable">$graph</span>- default( fontsize , 10 , edge );</span><br></pre></td></tr></table></figure></p><h1 id="Android-Studio-小技巧"><a href="#Android-Studio-小技巧" class="headerlink" title="Android Studio 小技巧"></a>Android Studio 小技巧</h1><p><a href="https://blog.csdn.net/myosotis5/article/details/79208707" target="_blank" rel="noopener">android studio 学习入门篇</a><br><a href="https://blog.csdn.net/qq_27093465/article/details/52857307" target="_blank" rel="noopener">查看类的继承关系</a></p><p>AndroidStudio 3.1.2 快捷键<br>AndroidStudio有个不好的地方是，快捷键太多了，很多快捷键功能会与系统功能冲突，导致快捷键功能实际不起作用，而不生效.</p><p>Ctrl + C:复制<br>Ctrl + Shift + C:复制绝对路径，在某个文件内，按此三个键，实现复制当前文件的路径<br>Ctrl + Shift + Alt +C :复制当前文件相对路径，得到的是当前项目文件夹下面的路径</p><p>查找与替换<br>Ctrl + F: 文件内查找<br>Ctrl + R: 文件内替换<br>F3：查找下一个<br>Shift + F3：查找前一个<br>Ctrl + Shift + F:当前项目中全局搜索<br>Ctrl + Shift + R:当前项目中全局替换</p><p>选择<br>Ctrl + W : 会选择鼠标附近的模块代码，一直点击的话，会一直扩大选择的范围<br>Ctrl + Shift + W: 对应的会缩小选择的范围</p><p>常用的：<br>Ctrl + F12：显示当前文件中，文件结构，也就是有哪些方法和变量<br>Ctrl + Shift + N : 在当前项目中查找文件<br>Ctrl + E :显示最近打开的文件<br>Ctrl + G :按照行号跳转<br>查找变量或者方法被使用的地方： 右键-&gt; Find Usages<br>显示变量或者方法被使用的地方： Ctrl + Alt + 7<br>Ctrl + Alt + H：可以查看当前方法被哪些地方调用到了，以及当前方法掉用了哪些函数.</p><p>Ctrl + +:展开方法<br>Ctrl + -：折叠方法<br>Ctrl + Shift + +:展开所有方法<br>Ctrl + Shift + -：折叠所有方法<br>Ctrl + Alt + I:模块代码自动对齐</p><p>Ctrl + D: 在下一行复制当前行代码<br>Ctrl + Shift + U: 整个单词的大小些切换，全部大写，或者全部小写<br>Ctrl + Shift + Alt + 2:显示文件夹和文件，点击后，直接通过文件夹的方式打开文件所在的路径.</p><p>文件对比： View -&gt; Compare With</p><p>AS 插件</p><p><a href="https://blog.csdn.net/zheng_weichao/article/details/72961791" target="_blank" rel="noopener">事半功倍：Android Studio 优秀插件汇总</a><br><a href="https://blog.csdn.net/m0_37358427/article/details/81131353" target="_blank" rel="noopener">最完整的Andriod studio插件整理</a> 有图形化展示插件的功能</p><h1 id="Visual-Studio-小技巧"><a href="#Visual-Studio-小技巧" class="headerlink" title="Visual Studio 小技巧"></a>Visual Studio 小技巧</h1><p><a href="https://www.cnblogs.com/happyzwt/p/7769129.html" target="_blank" rel="noopener">Visual Studio 2017 常用快捷键</a><br>Ubuntu Version 1.23.1 的快捷方式如下：<br>CTRL + P 打开某个文件或者打开某行，可以输入?，进行命令查询<br>CTRL + P 之后可以使用的命令如下:<br>a) :123 跳到当前文件的123行<br>   或者直接Ctrl + G 也可以跳到某行<br>b）#  列出了当前项目的所有方法，可以选择或者输入进入对应的方法<br>c) &gt; 可以执行某些命令,这些命令很好玩，<br>    比如第一个Add Curor above，会在当前行上面的一行增加一个光标，后面输入的字符，两行可以同时输入<br>    如果当前连接上了手机的话，Android: View Logcat,会在当前文件右边打开一个logcat的窗口，显示实时的log，非常有用<br>    还有很多其他的命令待探索，各种语言或者工具，都分别有一些小工具<br>d) edt 会显示当前打开了的所有的文件列表，我们可以输入文件名进行跳转，这个比CTRL+TAB更好一点，当打开的文件太多的时候，用TAB键很费力<br>e) ext 会进入安装了的插件管理<br>f) view 可以进行其他模块的跳转，比如说，打开文件界面，打开搜索界面，打开控制台界面等等<br>g) @ 可以@当前文件中的所有方法和变量，然后进行跳转<br>h) @: 可以调用当前文件中的所有方法和变量，然后进行跳转，和@的区别是，进行了归类，把方法放一起显示，变量放一起显示</p><p>ctrl + w  关闭当前打开的文件窗口<br>CTRL+TAB：活动窗体切换 （alt+tab：任务切换）<br>CTRL+SHIFT+TAB：上一个文档窗口<br>Ctrl+F: 在当前文件中查找 （Find）<br>Ctrl+Shift+F: 在当前项目中查找<br>F3: 查找下一个<br>Shift+F3: 查找上一个<br>Ctrl+H: 在当前文件中替换<br>Ctrl+Shift+H: 在当前项目中替换<br>Ctrl+K,C: 注释选定内容 （Comment）<br>Ctrl+K,U: 取消选定内容注释 （UnComment）<br>Ctrl+J：打开/关闭 Debug 控制台<br>Ctrl + Shift + Y :打开/关闭 控制台<br>View -&gt; Debug Control :打开/关闭 控制台</p><p>Ctrl + Shift + Y: 打开新的VS窗口<br>Ctrl + = 放大窗口<br>Ctrl + - 缩小窗口</p><p>shift+f12:查找所有引用（光标放在单词上，按Shift+F12）</p><p>Shift+Alt+箭头键:选择矩形文本，鼠标可以占多行，实现多行同时输入</p><p>鼠标右键功能：<br>鼠标选中，<br>右键-&gt; Peek Definition 变量或者方法 查找其定义<br>右键-&gt; Go to Definition,跳转到其声明或者定义的地方<br>右键-&gt; Find All Reference，查找所有相关的地方</p><p>实现两个文件的比较：<br>在左侧选中需要比较的文件1，右键-&gt; Select for Compare<br>在左侧选中需要比较的文件1，右键-&gt; Compare with Select</p><p>Ctrl + Atl + - :光标回到上次的位置<br>顶部状态栏-&gt; Go -&gt; Back : 光标回到上次的位置<br>Ctrl + Shift + - :光标回到下次的位置<br>顶部状态栏-&gt; Go -&gt; Back : 光标回到下次的位置</p><p>Ctrl + K,T 调整VS的颜色</p><p>VS 插件推荐：<br>插件搜索notepad++,sublimetext,markdown等，可以安装相关的工具<br>搜索Android，第一个Android插件需要安装</p><p>VS 插件的使用</p><ol><li>搜索自己需要的插件</li><li>Install</li><li>Reload 即可</li></ol><p>好用的VS 插件推荐</p><ol><li>Git History Diff  可以迅速的查看git提交记录</li></ol><p><a href="https://javahello.github.io/2018/02/26/ubuntu/vscode-java-environment/" target="_blank" rel="noopener">vscode Java 开发环境配置</a></p><h1 id="Eclipse-小技巧"><a href="#Eclipse-小技巧" class="headerlink" title="Eclipse 小技巧"></a>Eclipse 小技巧</h1><h1 id="Git-常用命令"><a href="#Git-常用命令" class="headerlink" title="Git 常用命令"></a>Git 常用命令</h1><ol><li>gitk工具命令：gitk</li></ol><h1 id="Java小技巧"><a href="#Java小技巧" class="headerlink" title="Java小技巧"></a>Java小技巧</h1><ol><li>判断字符串是否为空<br>if(TextUtils.isEmpty(plmn.trim())){}</li></ol><h1 id="USB-数据线调试"><a href="#USB-数据线调试" class="headerlink" title="USB 数据线调试"></a>USB 数据线调试</h1><p>1.解决error: device not found<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">All-Series:~$ adb root</span><br><span class="line">error: device not found</span><br></pre></td></tr></table></figure></p><p><a href="https://blog.csdn.net/zheng_weichao/article/details/77688569" target="_blank" rel="noopener">解决adb调试报错error:device not found</a></p><p>2.解决adb devices no permissions<br>手机插入数据线输入lsusb<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">All-Series:~$ lsusb</span><br><span class="line">Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub</span><br><span class="line">Bus 001 Device 002: ID 8087:8009 Intel Corp. </span><br><span class="line">Bus 002 Device 002: ID 8087:8001 Intel Corp. </span><br><span class="line">Bus 002 Device 003: ID 1c4f:0002 SiGma Micro Keyboard TRACER Gamma Ivory</span><br><span class="line">Bus 002 Device 004: ID 0101:0007  </span><br><span class="line">Bus 002 Device 005: ID 046d:c534 Logitech, Inc. </span><br><span class="line">Bus 002 Device 030: ID 201e:2871</span><br></pre></td></tr></table></figure></p><p>拔掉数据线，输入usb<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">All-Series:~$ lsusb</span><br><span class="line">Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</span><br><span class="line">Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub</span><br><span class="line">Bus 001 Device 002: ID 8087:8009 Intel Corp. </span><br><span class="line">Bus 002 Device 002: ID 8087:8001 Intel Corp. </span><br><span class="line">Bus 002 Device 003: ID 1c4f:0002 SiGma Micro Keyboard TRACER Gamma Ivory</span><br><span class="line">Bus 002 Device 004: ID 0101:0007  </span><br><span class="line">Bus 002 Device 005: ID 046d:c534 Logitech, Inc.</span><br></pre></td></tr></table></figure></p><p>发现少了Bus 002 Device 030: ID 201e:2871，这个就是我们的数据线的端口</p><p><a href="https://blog.csdn.net/jejay/article/details/65936662" target="_blank" rel="noopener">Linux 万能的android.rules文件 解决 adb devices no permissions</a></p><h1 id="关于Android-8-0"><a href="#关于Android-8-0" class="headerlink" title="关于Android 8.0"></a>关于Android 8.0</h1><p>Framework模块编译后,push framework.jar包不生效问题<br>需要将system/framework/arm目录里面对应的framework.oat framework.art framework.odex等三个文件删除<br>才能生效<br>对于push services.jar<br>需要删除arm和oat目录里面的对应文件</p><h1 id="Android-编译"><a href="#Android-编译" class="headerlink" title="Android 编译"></a>Android 编译</h1><ol><li><p>一条命令编译多个模块<br>make SystemUI;make Launcher3<br>make framework;make services;make Settings等等</p></li><li><p>写一个自动编译脚本</p></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;VIM-使用小技巧&quot;&gt;&lt;a href=&quot;#VIM-使用小技巧&quot; class=&quot;headerlink&quot; title=&quot;VIM 使用小技巧&quot;&gt;&lt;/a&gt;VIM 使用小技巧&lt;/h1&gt;&lt;h2 id=&quot;配置鼠标和显示行号&quot;&gt;&lt;a href=&quot;#配置鼠标和显示行号&quot; class
      
    
    </summary>
    
      <category term="Tools" scheme="http://yoursite.com/categories/Tools/"/>
    
    
      <category term="小技巧 工具 vim shell putty" scheme="http://yoursite.com/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7-%E5%B7%A5%E5%85%B7-vim-shell-putty/"/>
    
  </entry>
  
  <entry>
    <title>有用的网站</title>
    <link href="http://yoursite.com/UsefulWebsite/"/>
    <id>http://yoursite.com/UsefulWebsite/</id>
    <published>2015-06-29T06:06:59.000Z</published>
    <updated>2018-09-09T09:37:36.255Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Google和优土镜像"><a href="#Google和优土镜像" class="headerlink" title="Google和优土镜像"></a>Google和优土镜像</h1><p>Google<br><a href="http://ac.scmor.com" target="_blank" rel="noopener">http://ac.scmor.com</a><br><a href="https://google.jiongjun.cc" target="_blank" rel="noopener">https://google.jiongjun.cc</a><br><a href="https://g.zmirrordemo.com" target="_blank" rel="noopener">https://g.zmirrordemo.com</a><br><a href="https://www.gotype.tk" target="_blank" rel="noopener">https://www.gotype.tk</a><br><a href="http://www.hlhmf.com/" target="_blank" rel="noopener">http://www.hlhmf.com/</a></p><p>YouTube<br><a href="https://ytb-pc.zmirrordemo.com" target="_blank" rel="noopener">https://ytb-pc.zmirrordemo.com</a><br><a href="https://youtube.speeder.cf/" target="_blank" rel="noopener">https://youtube.speeder.cf/</a> （登陆密码和账号都是speeder.club）<br><a href="http://wall.qiqiblog.cn" target="_blank" rel="noopener">http://wall.qiqiblog.cn</a></p><h1 id="Chrome与crx扩展下载站"><a href="#Chrome与crx扩展下载站" class="headerlink" title="Chrome与crx扩展下载站"></a>Chrome与crx扩展下载站</h1><p>chrome下载<br><a href="https://repo.fdzh.org/chrome/exe/" target="_blank" rel="noopener">https://repo.fdzh.org/chrome/exe/</a><br><a href="http://www.chromeliulanqi.com/" target="_blank" rel="noopener">http://www.chromeliulanqi.com/</a></p><p>chrome扩展下载<br><a href="https://chrome-extension-downloader.com/" target="_blank" rel="noopener">https://chrome-extension-downloader.com/</a><br><a href="https://www.crx4chrome.com/" target="_blank" rel="noopener">https://www.crx4chrome.com/</a></p><h1 id="搜索引擎"><a href="#搜索引擎" class="headerlink" title="搜索引擎"></a>搜索引擎</h1><p>Qwant 法国的一个搜索引擎它声称不使用用户跟踪，不要将搜索结果个性化，以避免陷入过滤器泡泡中的用户。<br><a href="https://www.qwant.com/" target="_blank" rel="noopener">https://www.qwant.com/</a>  不用翻墙也能使用，中文的搜索也不错</p><p>Ecosia 主要搜索源是bing，必应国内版返回的数据和谐较多<br><a href="https://www.ecosia.org/" target="_blank" rel="noopener">https://www.ecosia.org/</a></p><p>Bing 国内可以直接访问，英文搜索结果也不错<br><a href="https://cn.bing.com/" target="_blank" rel="noopener">https://cn.bing.com/</a></p><p>Yandex 俄罗斯的搜索引擎，俄语较多<br><a href="https://www.yandex.com/" target="_blank" rel="noopener">https://www.yandex.com/</a></p><p>NAVER 韩国的搜索引擎，主要是韩语，不支持其他语言<br><a href="https://www.naver.com/" target="_blank" rel="noopener">https://www.naver.com/</a></p><p>TOR浏览器–可以访问暗网<br><a href="http://www.theonionrouter.com/projects/torbrowser.html.en" target="_blank" rel="noopener">http://www.theonionrouter.com/projects/torbrowser.html.en</a></p><h1 id="科学上网好好学习"><a href="#科学上网好好学习" class="headerlink" title="科学上网好好学习"></a>科学上网好好学习</h1><p><a href="https://user.xn--fhqzh392aa65vda8165c5gj.com/" target="_blank" rel="noopener">https://user.xn--fhqzh392aa65vda8165c5gj.com/</a><br>ShadowsocksR 简称SSR<br><a href="https://blog.csdn.net/tomlucky1024/article/details/79492329" target="_blank" rel="noopener">shadowsocks下载链接</a><br><a href="https://shadowsocks.org/en/download/clients.html" target="_blank" rel="noopener">https://shadowsocks.org/en/download/clients.html</a><br>win:<br><a href="https://github.com/shadowsocks/shadowsocks-windows/releases" target="_blank" rel="noopener">https://github.com/shadowsocks/shadowsocks-windows/releases</a><br>mac<br><a href="https://github.com/shadowsocks/ShadowsocksX-NG/releases" target="_blank" rel="noopener">https://github.com/shadowsocks/ShadowsocksX-NG/releases</a><br>Linux:<br><a href="https://github.com/shadowsocks/shadowsocks-qt5/wiki/Installation" target="_blank" rel="noopener">https://github.com/shadowsocks/shadowsocks-qt5/wiki/Installation</a><br><a href="https://jingyan.baidu.com/article/ae97a646fc2af0bbfd461d1e.html" target="_blank" rel="noopener">shadowsock 使用方法</a><br><a href="https://www.jianshu.com/p/0e46cbfbd773?utm_campaign=maleskine&amp;utm_content=note&amp;utm_medium=seo_notes&amp;utm_source=recommendation" target="_blank" rel="noopener">SSR功能介绍&amp;使用教程</a></p><p>第一步：下载SSR<br><a href="https://www.qcgzxw.cn/301.html#toc-1" target="_blank" rel="noopener">这里能下</a><br>第二步：<a href="https://loremwalker.github.io/fq-book/#/proxy/ss-ssr?id=%E9%85%8D%E7%BD%AE" target="_blank" rel="noopener">参照此教程</a><br>访问<a href="https://free-ss.tk/" target="_blank" rel="noopener">站点</a><br>第三步：SSR，右键-&gt; 服务器-&gt; 选择一个服务器<br>注意：把系统里面的国家和语言选为香港</p><h1 id="Chrome扩展与VPN名单表"><a href="#Chrome扩展与VPN名单表" class="headerlink" title="Chrome扩展与VPN名单表"></a>Chrome扩展与VPN名单表</h1><p>chrome代理插件清单<br>chrome代理插件    使用限制<br>SwitchyOmega    需要有服务器支持并手动配置参数<br>Google访问助手    需要设置360主页且只能访问Google服务<br>SurfEasyVPN    500M流量<br>windscribe    10G流量限制<br>privatix    免费<br>tuxler    免费<br>setupvpn    免费<br>Better net    完全免费<br>skyZip proxy    完全免费<br>ip unblock    完全免费<br>ininja Free Proxy    完全免费<br>tunnello    免费<br>VPN名单<br>vpn    使用限制<br>AstrillVPN    付费<br>VyprVPN    付费<br>zenmate    付费<br>nordvpn    付费<br>ExpressVPN    付费<br>SurfEasyVPN    500M流量<br>speedify    1G流量限制<br>windscribe    10G流量限制<br>protonvpn    免费限制较多但不限流量<br>试用3天高级账户，只限6个半小时<br>devpn    一小时更换一次密码<br>服务器以及协议上的限制<br>goldenkey    试用3天<br>Viscosity    试用30天需要配置文件<br>whoer    提供免费服务器<br>betternet    完全免费<br>psiphon    完全免费<br>gatevpn    完全免费并提供免费服务器还可自搭服务器<br>OpenVpn    需要提供服务器与配置文件<br>Cisco Anyconnect    需要提供服务器账户<br>附录</p><p>vpn配置站点<br><a href="https://whoer.net" target="_blank" rel="noopener">https://whoer.net</a><br><a href="https://www.vpnbook.com/freevpn" target="_blank" rel="noopener">https://www.vpnbook.com/freevpn</a><br><a href="https://www.freeopenvpn.org/en/connect.php" target="_blank" rel="noopener">https://www.freeopenvpn.org/en/connect.php</a><br><a href="https://www.vpnjantit.com/free-softether.html" target="_blank" rel="noopener">https://www.vpnjantit.com/free-softether.html</a></p><h1 id="DNS是纯净无污染防劫持的"><a href="#DNS是纯净无污染防劫持的" class="headerlink" title="DNS是纯净无污染防劫持的"></a>DNS是纯净无污染防劫持的</h1><p><a href="http://www.fundns.cn/" target="_blank" rel="noopener">http://www.fundns.cn/</a><br><a href="https://baidns.cn/" target="_blank" rel="noopener">https://baidns.cn/</a><br><a href="https://pdomo.me/" target="_blank" rel="noopener">https://pdomo.me/</a><br><a href="https://tuna.moe/help/dns/" target="_blank" rel="noopener">https://tuna.moe/help/dns/</a></p><h1 id="IPFS"><a href="#IPFS" class="headerlink" title="IPFS"></a>IPFS</h1><p>IPFS(InterPlanetary File System,星际文件系统)<br><a href="https://blog.csdn.net/dl88250/article/details/78579080/" target="_blank" rel="noopener">IPFS 入门笔记</a><br><a href="https://www.jianshu.com/p/8d78fdcdb7a3" target="_blank" rel="noopener">IPFS入门-搭建环境(Windows)</a></p><h1 id="SS-amp-amp-SSR-amp-v2ray分享"><a href="#SS-amp-amp-SSR-amp-v2ray分享" class="headerlink" title="SS&amp;&amp;SSR&amp;v2ray分享"></a>SS&amp;&amp;SSR&amp;v2ray分享</h1><p>使用须知<br>待定项<br>为贴子、博客或1-3个测试型的ss分享站点，可能存在长期未更新，有待观察<br>正常上网能访问的ss分享站点越来越少了，如果找不到满意的可去待定项看看<br>heroku的免费配额与限制<br>Network Bandwidth/流量: 2TB/month – Soft<br>Shared DB processing/并发数: Max 200msec per second CPU time – Soft<br>Dyno RAM usage/使用运行内存: 512MB – Hard<br>Slug Size/存储空间: 300MB – Hard<br>Request Length/请求时间: 30 seconds – Hard<br>提示Application error，是由于访问量占用内存溢出或因各项服务流量耗尽而停止服务</p><p>如果遇到打不开的站点请参考这个教程（推荐）<br><a href="https://loremwalker.github.io/fq-book/#/dns&amp;hosts/dnscrypt" target="_blank" rel="noopener">https://loremwalker.github.io/fq-book/#/dns&amp;hosts/dnscrypt</a><br><a href="https://loremwalker.github.io/fq-book/#/" target="_blank" rel="noopener">https://loremwalker.github.io/fq-book/#/</a> ★★★★★</p><p>v2ray<br><a href="https://v2ray.cat/" target="_blank" rel="noopener">https://v2ray.cat/</a></p><p>v2ray的账号分享站点目前较少</p><p>可用站点<br><a href="https://fast.ishadowx.net" target="_blank" rel="noopener">https://fast.ishadowx.net</a><br><a href="https://shadowsocksr.cat/" target="_blank" rel="noopener">https://shadowsocksr.cat/</a><br><a href="https://en.ss8.fun" target="_blank" rel="noopener">https://en.ss8.fun</a><br><a href="https://ss.freess.org/" target="_blank" rel="noopener">https://ss.freess.org/</a><br><a href="https://ss.ishadowx.com" target="_blank" rel="noopener">https://ss.ishadowx.com</a><br><a href="http://webosss.com/tool/socket" target="_blank" rel="noopener">http://webosss.com/tool/socket</a><br><a href="https://www.nutgeek.com/ssshadowsocks" target="_blank" rel="noopener">https://www.nutgeek.com/ssshadowsocks</a><br><a href="https://share-shadowsocksr.herokuapp.com" target="_blank" rel="noopener">https://share-shadowsocksr.herokuapp.com</a><br><a href="https://share-shadowsocks.herokuapp.com" target="_blank" rel="noopener">https://share-shadowsocks.herokuapp.com</a><br>待定项<br><a href="https://freessr.win" target="_blank" rel="noopener">https://freessr.win</a><br><a href="http://zgqx.gq/" target="_blank" rel="noopener">http://zgqx.gq/</a><br><a href="https://5l44.pw/" target="_blank" rel="noopener">https://5l44.pw/</a><br><a href="https://www.flyzy2005.com/fan-qiang/shadowsocks/free-ss-account/" target="_blank" rel="noopener">https://www.flyzy2005.com/fan-qiang/shadowsocks/free-ss-account/</a><br><a href="https://biulink.club/" target="_blank" rel="noopener">https://biulink.club/</a><br><a href="http://www.ssrfx.com" target="_blank" rel="noopener">http://www.ssrfx.com</a><br><a href="http://www.vpn168.tk" target="_blank" rel="noopener">http://www.vpn168.tk</a><br><a href="https://pdf-lib.org/Home/Details/2638" target="_blank" rel="noopener">https://pdf-lib.org/Home/Details/2638</a><br><a href="https://github.com/ssstk/freess" target="_blank" rel="noopener">https://github.com/ssstk/freess</a><br>镜像站点<br><a href="https://trial.ssbit.win" target="_blank" rel="noopener">https://trial.ssbit.win</a><br><a href="http://free-ss.tk" target="_blank" rel="noopener">http://free-ss.tk</a><br>科学访问<br><a href="https://www.ssrshare.com" target="_blank" rel="noopener">https://www.ssrshare.com</a><br><a href="https://get.ishadowx.net" target="_blank" rel="noopener">https://get.ishadowx.net</a><br><a href="https://get.freess.today" target="_blank" rel="noopener">https://get.freess.today</a><br><a href="https://tool.ssrshare.com/tool/free_ssr" target="_blank" rel="noopener">https://tool.ssrshare.com/tool/free_ssr</a><br><a href="https://doub.io" target="_blank" rel="noopener">https://doub.io</a><br><a href="https://free-ss.site" target="_blank" rel="noopener">https://free-ss.site</a><br><a href="https://global.ishadowx.net" target="_blank" rel="noopener">https://global.ishadowx.net</a><br><a href="http://www.52ssr.net/" target="_blank" rel="noopener">http://www.52ssr.net/</a><br><a href="http://i.wuw.red" target="_blank" rel="noopener">http://i.wuw.red</a><br><a href="https://freess.cx" target="_blank" rel="noopener">https://freess.cx</a><br><a href="https://free.yitianjianss.com" target="_blank" rel="noopener">https://free.yitianjianss.com</a><br><a href="http://freeoutline.org/zh" target="_blank" rel="noopener">http://freeoutline.org/zh</a><br>ssr订阅源<br><a href="https://www.nutgeek.cn/newsubscribe/" target="_blank" rel="noopener">https://www.nutgeek.cn/newsubscribe/</a><br><a href="https://prom-php.herokuapp.com/cloudfra_ssr.txt" target="_blank" rel="noopener">https://prom-php.herokuapp.com/cloudfra_ssr.txt</a><br><a href="http://share-shadowsocks.herokuapp.com/full/subscribe" target="_blank" rel="noopener">http://share-shadowsocks.herokuapp.com/full/subscribe</a><br><a href="http://share-shadowsocksr.herokuapp.com/subscribe?valid=1" target="_blank" rel="noopener">http://share-shadowsocksr.herokuapp.com/subscribe?valid=1</a><br><a href="https://raw.githubusercontent.com/ImLaoD/sub/master/ssrshare.com" target="_blank" rel="noopener">https://raw.githubusercontent.com/ImLaoD/sub/master/ssrshare.com</a><br>分享邮箱<br><a href="mailto:toyoooooooooooo@gmail.com" target="_blank" rel="noopener">toyoooooooooooo@gmail.com</a> (doub.io)<br><a href="mailto:ye515430@gmail.com" target="_blank" rel="noopener">ye515430@gmail.com</a> (yitianjianss)<br><a href="mailto:ss@rohankdd.com" target="_blank" rel="noopener">ss@rohankdd.com</a> (free-ss.site)<br>搜索相似的网站<br><a href="https://www.similarsites.com" target="_blank" rel="noopener">https://www.similarsites.com</a></p><p>已失效<br><a href="https://shadowsocksph.space" target="_blank" rel="noopener">https://shadowsocksph.space</a><br><a href="https://free.4kvpn.com" target="_blank" rel="noopener">https://free.4kvpn.com</a><br><a href="https://freess.pw" target="_blank" rel="noopener">https://freess.pw</a><br><a href="https://doub.loan" target="_blank" rel="noopener">https://doub.loan</a><br><a href="https://freemz.tk/t/5" target="_blank" rel="noopener">https://freemz.tk/t/5</a><br><a href="https://52ssr.cn" target="_blank" rel="noopener">https://52ssr.cn</a><br><a href="http://www.honsuv.com/?post=90" target="_blank" rel="noopener">http://www.honsuv.com/?post=90</a><br><a href="https://newdoub.com" target="_blank" rel="noopener">https://newdoub.com</a></p><h1 id="Web在线代理"><a href="#Web在线代理" class="headerlink" title="Web在线代理"></a>Web在线代理</h1><p>可用<br><a href="https://www.rabb.it" target="_blank" rel="noopener">https://www.rabb.it</a><br><a href="https://weboas.is/" target="_blank" rel="noopener">https://weboas.is/</a><br><a href="https://www.anyproxy.cn" target="_blank" rel="noopener">https://www.anyproxy.cn</a><br><a href="https://www.anyproxy.top" target="_blank" rel="noopener">https://www.anyproxy.top</a> (挂了)<br><a href="https://cn.bing.com/translator/" target="_blank" rel="noopener">https://cn.bing.com/translator/</a> (挂了)<br>代理网址服务器列表<br><a href="http://free-proxy.cz" target="_blank" rel="noopener">http://free-proxy.cz</a><br><a href="http://www.freeproxylists.net" target="_blank" rel="noopener">http://www.freeproxylists.net</a></p><h1 id="推荐使用SSH翻墙，教程及资源看这里"><a href="#推荐使用SSH翻墙，教程及资源看这里" class="headerlink" title="推荐使用SSH翻墙，教程及资源看这里"></a>推荐使用SSH翻墙，教程及资源看这里</h1><p>使用教程<br><a href="https://loremwalker.github.io/fq-book/#/proxy/SSH-Tunnel" target="_blank" rel="noopener">https://loremwalker.github.io/fq-book/#/proxy/SSH-Tunnel</a></p><p>网站<br><a href="https://skyssh.com" target="_blank" rel="noopener">https://skyssh.com</a><br><a href="https://sshdropbear.net/" target="_blank" rel="noopener">https://sshdropbear.net/</a><br><a href="https://speedssh.com/" target="_blank" rel="noopener">https://speedssh.com/</a><br><a href="https://fastssh.com/" target="_blank" rel="noopener">https://fastssh.com/</a><br><a href="https://www.mytunneling.com/" target="_blank" rel="noopener">https://www.mytunneling.com/</a><br><a href="https://createssh.com/" target="_blank" rel="noopener">https://createssh.com/</a><br><a href="https://bestvpnssh.com/" target="_blank" rel="noopener">https://bestvpnssh.com/</a><br><a href="https://fullssh.com/" target="_blank" rel="noopener">https://fullssh.com/</a><br><a href="https://www.sshagan.net/" target="_blank" rel="noopener">https://www.sshagan.net/</a><br><a href="https://sshfree.net/" target="_blank" rel="noopener">https://sshfree.net/</a><br><a href="https://www.portssh.com/" target="_blank" rel="noopener">https://www.portssh.com/</a><br><a href="https://www.jetssh.com/" target="_blank" rel="noopener">https://www.jetssh.com/</a><br><a href="https://sshudp.com/" target="_blank" rel="noopener">https://sshudp.com/</a><br><a href="http://cloudssh.us/" target="_blank" rel="noopener">http://cloudssh.us/</a><br><a href="http://free-ssh.xyz/" target="_blank" rel="noopener">http://free-ssh.xyz/</a><br><a href="https://sshkit.com/" target="_blank" rel="noopener">https://sshkit.com/</a><br>附工具下载<br><a href="https://www.bitvise.com/ssh-client-download" target="_blank" rel="noopener">https://www.bitvise.com/ssh-client-download</a></p><h1 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h1><p><a href="https://www.opera.com/zh-cn" target="_blank" rel="noopener">opera</a><br><a href="https://addons.opera.com/zh-cn/extensions/details/extension-source-viewer" target="_blank" rel="noopener">extension-source-viewer（opera的chrome扩展下载器）</a><br><a href="https://geti2p.net/zh/download" target="_blank" rel="noopener">i2p</a><br><a href="https://www.torproject.org/" target="_blank" rel="noopener">tor</a><br><a href="https://www.puffinbrowser.com/" target="_blank" rel="noopener">puffin</a></p><h1 id="听歌、观影、玩游戏"><a href="#听歌、观影、玩游戏" class="headerlink" title="听歌、观影、玩游戏"></a>听歌、观影、玩游戏</h1><p>听歌<br><a href="http://beemp3s.org" target="_blank" rel="noopener">http://beemp3s.org</a><br><a href="https://soundcloudmp3.org/zh" target="_blank" rel="noopener">https://soundcloudmp3.org/zh</a><br><a href="http://www.51ape.com" target="_blank" rel="noopener">http://www.51ape.com</a><br>观影<br><a href="http://ihd.me" target="_blank" rel="noopener">http://ihd.me</a><br><a href="http://www.dytt8.net/" target="_blank" rel="noopener">http://www.dytt8.net/</a> 电影天堂<br><a href="https://genvideos.org" target="_blank" rel="noopener">https://genvideos.org</a><br><a href="http://www.kupian.cc" target="_blank" rel="noopener">http://www.kupian.cc</a><br><a href="http://www.77yingshi.top" target="_blank" rel="noopener">http://www.77yingshi.top</a><br><a href="https://theporndude.com/" target="_blank" rel="noopener">https://theporndude.com/</a><br>游戏<br><a href="http://www.y8.com" target="_blank" rel="noopener">http://www.y8.com</a><br><a href="http://www.kguowai.com/news/593.html" target="_blank" rel="noopener">http://www.kguowai.com/news/593.html</a><br><a href="https://www.miniclip.com/games/dino-strike/en/" target="_blank" rel="noopener">https://www.miniclip.com/games/dino-strike/en/</a></p><h1 id="导航网址"><a href="#导航网址" class="headerlink" title="导航网址"></a>导航网址</h1><p><a href="https://botw.org/" target="_blank" rel="noopener">https://botw.org/</a><br><a href="https://www.seeseed.com/" target="_blank" rel="noopener">https://www.seeseed.com/</a><br><a href="http://www.kguowai.com/" target="_blank" rel="noopener">http://www.kguowai.com/</a><br><a href="http://www.kanguowai.com/" target="_blank" rel="noopener">http://www.kanguowai.com/</a><br><a href="https://www.ifanqiang.com/" target="_blank" rel="noopener">https://www.ifanqiang.com/</a></p><h1 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h1><p>Demo镜像，目前不可用，<br>来自哪里：浙江大学</p><p>Google<br>网页搜索 <a href="https://g.zmirrordemo.com" target="_blank" rel="noopener">https://g.zmirrordemo.com</a>  Google搜索可以直接打开<br>Scholar <a href="https://g.zmirrordemo.com/scholar" target="_blank" rel="noopener">https://g.zmirrordemo.com/scholar</a><br>Image <a href="https://g.zmirrordemo.com/imghp" target="_blank" rel="noopener">https://g.zmirrordemo.com/imghp</a><br>Gmail <a href="https://g.zmirrordemo.com/gmail" target="_blank" rel="noopener">https://g.zmirrordemo.com/gmail</a> (请选择”使用基本HTML”版本)</p><p>中文维基<br>(PC &amp; Mobile) <a href="https://g.zmirrordemo.com/wiki" target="_blank" rel="noopener">https://g.zmirrordemo.com/wiki</a></p><p>Youtube<br>PC Only <a href="https://ytb-pc.zmirrordemo.com" target="_blank" rel="noopener">https://ytb-pc.zmirrordemo.com</a><br>　　Youtube访问需要密码, 请解密此串得到密码WmhlamlhbmdVbml2ZXJzaXR5 如果您无法解密, 很抱歉Youtube镜像不对您开放<br>Mobile Only <a href="https://ytb-mobile.zmirrordemo.com" target="_blank" rel="noopener">https://ytb-mobile.zmirrordemo.com</a> 暂不开放<br>　　Youtube Mobile 不支持iOS<br><!--Please input your code here please decode that string for password:ZhejiangUniversityWhere do you know this software/website? Chinese is supported. 你从哪知道的本程序/网站 :浙江大学Please input your university/ 请输入您所在的高校：浙江大学--></p><p>Twitter<br>PC Only <a href="https://t-pc.zmirrordemo.com" target="_blank" rel="noopener">https://t-pc.zmirrordemo.com</a><br>Mobile Only <a href="https://t-mobile.zmirrordemo.com" target="_blank" rel="noopener">https://t-mobile.zmirrordemo.com</a></p><p>Instagram<br>PC &amp; Mobile <a href="https://in.zmirrordemo.com" target="_blank" rel="noopener">https://in.zmirrordemo.com</a></p><p>Facebook<br>PC Only <a href="https://fb.zmirrordemo.com" target="_blank" rel="noopener">https://fb.zmirrordemo.com</a></p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://github.com/loremwalker/WebSiteUseful" target="_blank" rel="noopener">WebSiteUseful</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Google和优土镜像&quot;&gt;&lt;a href=&quot;#Google和优土镜像&quot; class=&quot;headerlink&quot; title=&quot;Google和优土镜像&quot;&gt;&lt;/a&gt;Google和优土镜像&lt;/h1&gt;&lt;p&gt;Google&lt;br&gt;&lt;a href=&quot;http://ac.scmor
      
    
    </summary>
    
      <category term="Tools - UsefulWebsite - google" scheme="http://yoursite.com/categories/Tools-UsefulWebsite-google/"/>
    
    
      <category term="Tools - UsefulWebsite - google" scheme="http://yoursite.com/tags/Tools-UsefulWebsite-google/"/>
    
  </entry>
  
  <entry>
    <title>windows批处理 BAT技巧</title>
    <link href="http://yoursite.com/Window-Bat/"/>
    <id>http://yoursite.com/Window-Bat/</id>
    <published>2015-06-29T03:49:50.000Z</published>
    <updated>2018-07-01T10:38:39.635Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h1><p>Windows操作系统中，cmd可以执行bat格式的文件，可以自己编写bat文件完成一些重复性的工作。</p><h1 id="用批处理命令批量获取指定文件夹内的文件名"><a href="#用批处理命令批量获取指定文件夹内的文件名" class="headerlink" title="用批处理命令批量获取指定文件夹内的文件名"></a>用批处理命令批量获取指定文件夹内的文件名</h1><p>dir /s /w &gt;&gt;catalog.txt<br>::/s 表示只显示系统文件<br>::/w 表示只显示文件名，至于文件大小以及建立的日期和时间都省略<br>::dir *.doc /s /w &gt;&gt; catalog.txt 表示仅生成后缀为.doc文件</p><h1 id="BAT文件注释符"><a href="#BAT文件注释符" class="headerlink" title="::BAT文件注释符"></a>::BAT文件注释符</h1><p>:: 注释内容（第一个冒号后也可以跟任何一个非字母数字的字符）<br>rem 注释内容（不能出现重定向符号和管道符号）<br>:注释内容（注释文本不能与已有标签重名）<br>%注释内容(可以用作行间注释，不能出现重定向符号和管道符号)%</p><p>rem 指定存放文件的目录<br>set originPath=C:\Documents and Settings\zhoudeshui\Desktop\临时文件</p><p>:@echo<br>:　　dir c:<em>.</em> &gt;a.txt　　　　　　　<br>:　　call c:\ucdos\ucdos.bat　　　　<br>:　　echo 你好 　　　　　　　　　　<br>:　　pause 　　　　　　　　　　　　</p><p>@echo off<br>for /f “delims=\” %%a in (‘dir /b /a-d /o-d “%originPath%<em>.</em>”’) do (<br>echo %%a &gt; aa.txt<br>)<br>pause</p><h1 id="使用bat-完成Android-log的自动抓取"><a href="#使用bat-完成Android-log的自动抓取" class="headerlink" title="使用bat 完成Android log的自动抓取"></a>使用bat 完成Android log的自动抓取</h1><p>可以将抓log的命令，做成一个合集，放在一个bat文件中，一步运行，抓取所有log。适合于工厂产线抓log。<br><a href="https://blog.csdn.net/maetelibom/article/details/51480980" target="_blank" rel="noopener">一种通过自动脚本抓取Android 手机log的方法</a><br><a href="https://blog.csdn.net/xxm282828/article/details/46433095" target="_blank" rel="noopener">使用脚本导出android 手机log</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概览&quot;&gt;&lt;a href=&quot;#概览&quot; class=&quot;headerlink&quot; title=&quot;概览&quot;&gt;&lt;/a&gt;概览&lt;/h1&gt;&lt;p&gt;Windows操作系统中，cmd可以执行bat格式的文件，可以自己编写bat文件完成一些重复性的工作。&lt;/p&gt;
&lt;h1 id=&quot;用批处理命令
      
    
    </summary>
    
      <category term="批处理" scheme="http://yoursite.com/categories/%E6%89%B9%E5%A4%84%E7%90%86/"/>
    
    
      <category term="批处理" scheme="http://yoursite.com/tags/%E6%89%B9%E5%A4%84%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>ADB (Android Debug Bridge)常用命令</title>
    <link href="http://yoursite.com/ADB-Android-Debug-Bridge/"/>
    <id>http://yoursite.com/ADB-Android-Debug-Bridge/</id>
    <published>2015-06-29T03:27:47.000Z</published>
    <updated>2018-09-03T08:52:18.993Z</updated>
    
    <content type="html"><![CDATA[<!--修改记录：1.2016年完成第一版2.2018年修订完成目标：分析adb的源码    --><p>ADB (Android Debug Bridge)</p><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p><a href="https://android.googlesource.com/platform/system/core/+/master/adb/" target="_blank" rel="noopener">Android中Adb源码</a></p><p><strong>adb 源码分析： 待补充</strong></p><p>  <a href="https://blog.csdn.net/u010223349/article/details/41120255" target="_blank" rel="noopener">ADB实现解析</a><br>  <a href="https://blog.csdn.net/ysh149216447/article/details/53334015" target="_blank" rel="noopener">Android adb实现原理</a></p><p>说明：下面一些命令需要有root权限才能执行成功<br>Windows环境下的adb<br>快速启动dos窗口执行adb：<br>1. adb.exe所在路径添加到系统环境变量中<br>2. 配置快捷键启动dos<br>进入C:\WINDOWS\system32目录下，找到cmd.exe.<br>右击菜单 “发送到” -> 桌面快捷方式。<br>在桌面上右击”快捷方式 到 cmd.exe” -&gt; “属性” -&gt; “快捷方式”页<br>-&gt; 光标高亮”快捷键” -> 按下自定义快捷键 (如：Ctrl + Alt + Z)</p><p>任何情况下，按下Ctrl + Alt + Z启动dos窗口就可以执行adb命令了</p><h1 id="查看设备连接状态-系列"><a href="#查看设备连接状态-系列" class="headerlink" title="查看设备连接状态 系列"></a>查看设备连接状态 系列</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">adb get-serialno 获取设备的ID和序列号serialNumber  </span><br><span class="line">adb devices 查询当前计算机上连接那些设备（包括模拟器和手机），输出格式: \[serialNumber\] \[state\]  </span><br><span class="line">adb get-state 查看模拟器/设施的当前状态.</span><br></pre></td></tr></table></figure><p>说明：<br>序列号[serialNumber]——由adb创建的一个字符串，这个字符串通过自己的控制端口-<br>唯一地识别一个模拟器/设备实例。一个序列号的例子： emulator-5554</p><h1 id="发送命令到设备-系列"><a href="#发送命令到设备-系列" class="headerlink" title="发送命令到设备 系列"></a>发送命令到设备 系列</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">adb \[-d|-e|-s \]  </span><br><span class="line">-d 发送命令给usb连接的设备  </span><br><span class="line">-e 发送命令到模拟器设备  </span><br><span class="line">-s 发送命令到指定设备</span><br></pre></td></tr></table></figure><p>如启动手机设备shell: adb -d shell</p><p>adb forward 发布端口,可以设置任意的端口号，<br>做为主机向模拟器或设备的请求端口。如：adb forward tcp:5555 tcp:8000</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">adb reboot 重启手机  </span><br><span class="line">adb remount 将system分区重新挂载为可读写分区  </span><br><span class="line">adb <span class="built_in">kill</span>-server 终止adb服务进程  </span><br><span class="line">adb start-server 重启adb服务进程  </span><br><span class="line">adb root 已root权限重启adb服务  </span><br><span class="line">adb <span class="built_in">wait</span>-for-device 在模拟器/设备连接之前把命令转载在adb的命令器中  </span><br><span class="line">adb jdwp 查看指定的设施的可用的JDWP信息.  Java Debug Wire Protocol (JDWP, Java调试线协议)是</span><br><span class="line">可以用 forward jdwp: 端口映射信息来连接指定的JDWP进程.例如：  </span><br><span class="line">adb forward tcp:8000 jdwp:472  </span><br><span class="line">jdb -attach localhost:8000</span><br></pre></td></tr></table></figure><h1 id="adb-shell-am-系列"><a href="#adb-shell-am-系列" class="headerlink" title="adb shell am 系列"></a>adb shell am 系列</h1><p>am命令可以启动应用程序<br>adb shell am start [options] <intent><br>作用：启动一个activity<br>举例：adb shell am start -a com.lt.test.action.SECOND<br>举例：adb shell am start -n com.lt.test/.MyActivity</intent></p><p>adb shell am startservice [options] <intent><br>作用：启动一个service<br>举例：adb shell am startservice -a com.lt.test.action.ONESERVICE<br>举例：adb shell am startservice -n com.lt.test/.MyService</intent></p><p>adb shell am force-stop <package><br>作用：强制关闭一个应用程序<br>举例：adb shell am force-stop com.lt.test</package></p><p>adb shell am broadcast [options] <intent><br>作用：发送一个广播<br>举例：adb shell am broadcast -a “action_finish” （发送一个广播去关闭一个activity）<br>举例：adb shell am broadcast -a android.intent.action.MASTER_CLEAR（恢复出厂设置的方法，会清除内存所有内容）<br>举例：adb shell am broadcast -n com.lt.test/.MyBroadcast</intent></p><h1 id="adb-shell-pm-系列"><a href="#adb-shell-pm-系列" class="headerlink" title="adb shell pm 系列"></a>adb shell pm 系列</h1><p>adb shell pm list packages [options] <intent><br>作用：列举出所有包含<intent>的package<br>举例：adb shell pm list packages com.lt<br>说明：[options]与<intent>参见 <a href="http://developer.android.com/tools/help/adb.html#pm" target="_blank" rel="noopener">http://developer.android.com/tools/help/adb.html#pm</a></intent></intent></intent></p><h1 id="adb-shell-input-系列"><a href="#adb-shell-input-系列" class="headerlink" title="adb shell input 系列"></a>adb shell input 系列</h1><p>adb shell input text 向设备输入文本（光标所在的文本框）<br>adb shell input keyevent 向设备发送按键事件<br>如：<br>在编辑短信时，往文本框输入文本：adb shell input text “hello”<br>向手机发送键值回Home：adb shell input keyevent 3<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">event\_code 参考view/KeyEvent.java中的 KEYCODE\_*  </span><br><span class="line">public static final int KEYCODE\_SOFT\_LEFT = 1;  </span><br><span class="line">public static final int KEYCODE\_SOFT\_RIGHT = 2;  </span><br><span class="line">public static final int KEYCODE_HOME = 3;  </span><br><span class="line">public static final int KEYCODE_BACK = 4;  </span><br><span class="line">public static final int KEYCODE_CALL = 5;  </span><br><span class="line">public static final int KEYCODE_ENDCALL = 6;</span><br></pre></td></tr></table></figure></p><h1 id="adb-shell-install-系列"><a href="#adb-shell-install-系列" class="headerlink" title="adb shell install 系列"></a>adb shell install 系列</h1><p>安装卸载 系列<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">adb install \[-l\] \[-r\] - push this package file to the device and install it  </span><br><span class="line">(‘-l’ means forward-lock the app)  </span><br><span class="line">(‘-r’ means reinstall the app, keeping its data)  </span><br><span class="line">adb uninstall \[-k\] - remove this app package from the device  </span><br><span class="line">(‘-k’ means keep the data and cache directories)</span><br></pre></td></tr></table></figure></p><p>如：<br>adb shell -r install d:\hello.apk<br>adb shell unstall com.huawei.hello<br>说明：如果带-r选项重新安装apk时，安装在 /data/local/tmp/目录下，手机重启后还是使用原来的apk.</p><h1 id="文件操作-系列"><a href="#文件操作-系列" class="headerlink" title="文件操作 系列"></a>文件操作 系列</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb push - copy file/dir to device  </span><br><span class="line">adb pull - copy file/dir from device</span><br></pre></td></tr></table></figure><h1 id="基本linux-shell命令-系列"><a href="#基本linux-shell命令-系列" class="headerlink" title="基本linux shell命令 系列"></a>基本linux shell命令 系列</h1><p>adb shell [command]<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">ls 列出目录下的文件和文件夹  </span><br><span class="line"><span class="built_in">cd</span> 切换目录  </span><br><span class="line">rm 删除目录和文件  </span><br><span class="line">cat 查看文件内容  </span><br><span class="line">ps 可以看那个进程再跑  </span><br><span class="line">ps -x \[PID\] 查看单个进程的状态  </span><br><span class="line">top 可以看那个进程的占用率最高  </span><br><span class="line">su 切换到root用户  </span><br><span class="line"><span class="built_in">kill</span> \[pid\] 杀死一个进程  </span><br><span class="line">chmod 777 修改该文件为可执行权限</span><br></pre></td></tr></table></figure></p><p>从framework/base/cmds中查到的一些常用的命令：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">adb shell am stack list 列出AM堆栈信息</span><br><span class="line">adb shell appops  应用权限授权管理框架,可以使用AppOps将其某项强制要求权限忽略掉</span><br><span class="line">adb shell appwidget 主要是设置apk是否有放在窗口的权限</span><br><span class="line">adb shell bmgr 备份用</span><br><span class="line">adb shell content</span><br><span class="line">adb shell dpm</span><br></pre></td></tr></table></figure></p><ol><li>打印某个进程中所有线程的backtrace<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell debuggerd -b pid</span><br></pre></td></tr></table></figure></li></ol><p>用于分析死锁</p><ol start="2"><li>打印系统所有注册的服务<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell service list</span><br></pre></td></tr></table></figure></li></ol><p>用于查看service是否注册成功</p><ol start="3"><li>查看某个应用时32位还是64位<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">adb shell ps --api</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">详细使用情况可以登录一台Linux服务器在shell下查看帮助手册, man</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看系统状态和信息 系列  </span></span><br><span class="line">``` bash</span><br><span class="line">adb shell procrank 查询各进程内存使用情况  </span><br><span class="line">adb shell service list 查看services信息  </span><br><span class="line">adb shell cat /proc/meminfo 查看当前的内存情况  </span><br><span class="line">adb shell cat /proc/cpuinfo 查看CPU信息（硬件）  </span><br><span class="line">adb shell cat /proc/iomem 查看IO内存分区</span><br><span class="line"></span><br><span class="line">adb shell getprop 列出系统所有属性  </span><br><span class="line">adb shell getprop | findstr “gsm” 列出包含gsm的属性  </span><br><span class="line">adb shell setprop 修改系统属性</span><br><span class="line"></span><br><span class="line">adb shell sqlite3 可以执行sql语句查看数据库信息， 具体使用情况待调查</span><br><span class="line"></span><br><span class="line">adb shell dmesg 查询内核缓冲区信息  </span><br><span class="line">adb shell dumpstate 各类信息，比如进程信息，内存信息，进程是否异常，kernnel的<span class="built_in">log</span>等  </span><br><span class="line">adb shell dumpcrash  </span><br><span class="line">adb shell dumpsys 查询所有service的状态</span><br></pre></td></tr></table></figure></li></ol><h1 id="获取屏幕大小和密度"><a href="#获取屏幕大小和密度" class="headerlink" title="获取屏幕大小和密度"></a>获取屏幕大小和密度</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">获取屏幕大小adb shell wm size</span><br><span class="line">获取屏幕密度adb shell wm density</span><br></pre></td></tr></table></figure><h1 id="adb-shell-dumpsys-系列"><a href="#adb-shell-dumpsys-系列" class="headerlink" title="adb shell dumpsys 系列"></a>adb shell dumpsys 系列</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">adb shell dumpsys activity top //查看当前运行Activity信息</span><br><span class="line">adb shell dumpsys package [pkgname] //查看指定包名应用详细信息，相当于AndroidManifest.xml中的内容</span><br><span class="line">adb shell dumpsys meminfo [pname/pid] //查看指定进程名或者进程id的内存信息</span><br><span class="line">adb shell dumpsys dbinfo [pkgname] //查看指定包名应用的数据库调用信息</span><br><span class="line">adb shell dumpstate 各类信息，比如进程信息，内存信息，进程是否异常，kernnel的<span class="built_in">log</span>等</span><br><span class="line">adb shell dumpcrash    不能用</span><br><span class="line">adb shell dumpsys  列出所有的dump信息</span><br><span class="line">adb shell dumpsys | grep <span class="string">"DUMP OF SERVICE"</span> 列出所有dumpsys支持的命令</span><br><span class="line">adb shell dumpsys service  不能用</span><br><span class="line">adb shell dumpsys service list  不能用</span><br><span class="line">adb shell service list 列出所有服务</span><br><span class="line"></span><br><span class="line">adb shell dumpsys activity  </span><br><span class="line">adb shell dumpsys window  </span><br><span class="line">adb shell dumpsys cpuinfo  </span><br><span class="line">adb shell dumpsys battery  </span><br><span class="line">adb shell dumpsys package   里面有userId</span><br><span class="line">adb shell dumpsys package  -h  </span><br><span class="line">adb shell dumpsys activity –h</span><br></pre></td></tr></table></figure><h1 id="adb-shell-截屏和录屏"><a href="#adb-shell-截屏和录屏" class="headerlink" title="adb shell 截屏和录屏"></a>adb shell 截屏和录屏</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell screencap -p /sdcard/1.png  //截图保存</span><br><span class="line">adb shell screenrecord /sdcard/1.mp4  //录屏保存</span><br></pre></td></tr></table></figure><h1 id="Log-系列"><a href="#Log-系列" class="headerlink" title="Log 系列"></a>Log 系列</h1><p>adb logcat [ ] - View device log</p><h2 id="查看可用日志缓冲区"><a href="#查看可用日志缓冲区" class="headerlink" title="查看可用日志缓冲区:"></a>查看可用日志缓冲区:</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">adb logcat -s tag // 查看tag的日志</span><br><span class="line">adb logcat -b radio — 查看缓冲区的相关的信息.  </span><br><span class="line">adb logcat -b events — 查看和事件相关的的缓冲区.  </span><br><span class="line">adb logcat -b main — 查看主要的日志缓冲区</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb logcat -v threadtime</span><br><span class="line">adb logcat -v time</span><br></pre></td></tr></table></figure><h2 id="过滤日志输出"><a href="#过滤日志输出" class="headerlink" title="过滤日志输出:"></a>过滤日志输出:</h2><p>过滤器语句按照下面的格式描tag:priority … , tag 表示是标签, priority 是表示标签的报告的最低等级<br>adb logcat *:W 显示优先级为warning或更高的日志信息<br>adb logcat ActivityManager:I MyApp:D *:S</p><p>日志的标签是系统部件原始信息的一个简要的标志。（比如：“View”就是查看系统的标签）.<br>优先级有下列集中，是按照从低到高顺利排列的:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">V — Verbose (lowest priority)  </span><br><span class="line">D — Debug  </span><br><span class="line">I — Info  </span><br><span class="line">W — Warning  </span><br><span class="line">E — Error  </span><br><span class="line">F — Fatal  </span><br><span class="line">S — Silent (highest priority, on <span class="built_in">which</span> nothing is ever printed)</span><br></pre></td></tr></table></figure></p><p>如果你电脑上运行logcat ，相比在远程adb shell端，你还可以为环境变量ANDROID_LOG_TAGS :输入一个参数来设置默认的过滤<br>export ANDROID_LOG_TAGS=”ActivityManager:I MyApp:D *:S”<br>需要注意的是ANDROID_LOG_TAGS 过滤器如果通过远程shell运行logcat 或用adb shell logcat 来运行模拟器/设备不能输出日志.</p><h2 id="控制日志输出格式"><a href="#控制日志输出格式" class="headerlink" title="控制日志输出格式:"></a>控制日志输出格式:</h2><p>日志信息包括了许多元数据域包括标签和优先级。可以修改日志的输出格式，所以可以显示出特定的元数据域。可以通过 -v 选项得到格式化输出日志的相关信息.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">brief — Display priority/tag and PID of originating process (the default format).  </span><br><span class="line">process — Display PID only.  </span><br><span class="line">tag — Display the priority/tag only.  </span><br><span class="line">thread — Display process:thread and priority/tag only.  </span><br><span class="line">raw — Display the raw <span class="built_in">log</span> message, with no other metadata fields.  </span><br><span class="line">time — Display the date, invocation time, priority/tag, and PID of the originating process.  </span><br><span class="line">long — Display all metadata fields and separate messages with a blank lines.</span><br></pre></td></tr></table></figure><p>当启动了logcat ，你可以通过-v 选项来指定输出格式:</p><p>[adb] logcat [-v ]<br>下面是用 thread 来产生的日志格式:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb logcat -v thread</span><br></pre></td></tr></table></figure><p>需要注意的是你只能-v 选项来规定输出格式 option.</p><h2 id="Logcat命令列表"><a href="#Logcat命令列表" class="headerlink" title="Logcat命令列表"></a>Logcat命令列表</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-b 加载一个可使用的日志缓冲区供查看，比如event 和radio . 默认值是main 。具体查看Viewing Alternative Log Buffers.  </span><br><span class="line">-c 清楚屏幕上的日志.  </span><br><span class="line">-d 输出日志到屏幕上.  </span><br><span class="line">-f 指定输出日志信息的 ，默认是stdout .  </span><br><span class="line">-g 输出指定的日志缓冲区，输出后退出.  </span><br><span class="line">-n 设置日志的最大数目 .，默认值是4，需要和 -r 选项一起使用。  </span><br><span class="line">-r 每 时输出日志，默认值为16，需要和-f 选项一起使用.  </span><br><span class="line">-s 设置默认的过滤级别为silent.  </span><br><span class="line">-v 设置日志输入格式，默认的是brief 格式，要知道更多的支持的格式，参看Controlling Log Output Format</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb bugreport - <span class="built_in">return</span> all information from the device  </span><br><span class="line">that should be included <span class="keyword">in</span> a bug report.</span><br></pre></td></tr></table></figure><p>———–其他 ———–</p><p>模拟器使用镜像sdcard<br>用SDK里的mksdcard工具来创建FAT32磁盘镜像并在模拟器启动时加载它。这样创建镜像：? mksdcard ,<br>比如我要创建一个64M的SD卡模拟文件，文件路径是在D:\workspace\sdcard.img<br>mksdcard 64000000 D:\workspace\sdcard.img</p><p>Emulator –sdcard D:\workspace\sdcard.img<br>或者在eclipse的run菜单的open run dialog对话框中配置启动参数。</p><h1 id="adb-shell-top-系列"><a href="#adb-shell-top-系列" class="headerlink" title="adb shell top 系列"></a>adb shell top 系列</h1><p>top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">Usage: top \[ -m max\_procs \] \[ -n iterations \] \[ -d delay \] \[ -s sort\_column \] \[ -t \] \[ -h \]  </span><br><span class="line">-m num Maximum number of processes to display.  //最多显示m个进程</span><br><span class="line">-n num Updates to show before exiting.   //刷新次数</span><br><span class="line">-d num Seconds to <span class="built_in">wait</span> between updates.   //刷新间隔时间（默认5秒）</span><br><span class="line">-s col Column to sort by (cpu,vss,rss,thr).  //按哪列排序 </span><br><span class="line">-t Show threads instead of processes.  //显示线程信息而不是进程</span><br><span class="line">-h Display this <span class="built_in">help</span> screen.  //显示帮助文档</span><br><span class="line"></span><br><span class="line">**_*****_** simple selection **_*_** **_*****_** selection by list **_*_**  </span><br><span class="line">-A all processes -C by <span class="built_in">command</span> name  </span><br><span class="line">-N negate selection -G by real group ID (supports names)  </span><br><span class="line">-a all w/ tty except session leaders -U by real user ID (supports names)  </span><br><span class="line">-d all except session leaders -g by session OR by effective group name  </span><br><span class="line">-e all processes -p by process ID  </span><br><span class="line">T all processes on this terminal -s processes <span class="keyword">in</span> the sessions given  </span><br><span class="line">a all w/ tty, including other users -t by tty  </span><br><span class="line">g OBSOLETE – DO NOT USE -u by effective user ID (supports names)  </span><br><span class="line">r only running processes U processes <span class="keyword">for</span> specified users  </span><br><span class="line">x processes w/o controlling ttys t by tty  </span><br><span class="line">**_*****_** output format **_******_** **_\*\*\*_** long options **_*****_**  </span><br><span class="line">-o,o user-defined -f full –Group –User –pid –cols –ppid  </span><br><span class="line">-j,j job control s signal –group –user –sid –rows –info  </span><br><span class="line">-O,O preloaded -o v virtual memory –cumulative –format –deselect  </span><br><span class="line">-l,l long u user-oriented –sort –tty –forest –version  </span><br><span class="line">-F extra full X registers –heading –no-heading –context  </span><br><span class="line">**_*****_** misc options **_*_**  </span><br><span class="line">-V,V show version L list format codes f ASCII art forest  </span><br><span class="line">-m,m,-L,-T,H threads S children <span class="keyword">in</span> sum -y change -l format  </span><br><span class="line">-M,Z security data c <span class="literal">true</span> <span class="built_in">command</span> name -c scheduling class  </span><br><span class="line">-w,w wide output n numeric WCHAN,UID -H process hierarchy</span><br></pre></td></tr></table></figure></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&gt; adb shell top</span><br><span class="line"> </span><br><span class="line">User 13%, System 5%, IOW 0%, IRQ 0%</span><br><span class="line">User 85 + Nice 0 + Sys 37 + Idle 509 + IOW 0 + IRQ 0 + SIRQ 0 = 631   //第一组数据</span><br><span class="line"> </span><br><span class="line">  PID PR CPU% S  <span class="comment">#THR     VSS     RSS PCY UID      Name    //第二组数据</span></span><br><span class="line">22205  0  13% S    56 423416K  88160K  <span class="built_in">fg</span> u0_a92   com.tmall.wireless</span><br><span class="line">24310  1   2% R     1   1232K    536K     root     top</span><br><span class="line">22600  0   1% S    46 341712K  40872K  <span class="built_in">fg</span> u0_a90   com.wandoujia.phoenix2.usbproxy</span><br><span class="line">31125  1   1% S    31 319976K  33284K  <span class="built_in">fg</span> u0_a74   com.android.Chinpower</span><br><span class="line"> 1533  0   1% S    32  67320K  20552K  <span class="built_in">fg</span> system   /system/bin/surfaceflinger</span><br><span class="line"> 1852  0   1% S   112 445876K  80304K  <span class="built_in">fg</span> system   system_server</span><br><span class="line">    ...</span><br><span class="line">   10  0   0% S     1      0K      0K     root     watchdog/0</span><br><span class="line">   16  1   0% S     1      0K      0K     root     khelper</span><br><span class="line">   22  1   0% S     1      0K      0K     root     suspend_sys_syn</span><br><span class="line">   23  1   0% S     1      0K      0K     root     <span class="built_in">suspend</span></span><br></pre></td></tr></table></figure><p>第一组数据的含义：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">User  处于用户态的运行时间，不包含优先值为负进程 </span><br><span class="line">Nice  优先值为负的进程所占用的CPU时间 </span><br><span class="line">Sys   处于核心态的运行时间 </span><br><span class="line">Idle  除IO等待时间以外的其它等待时间 </span><br><span class="line">IOW   IO等待时间 </span><br><span class="line">IRQ   硬中断时间 </span><br><span class="line">SIRQ  软中断时间</span><br></pre></td></tr></table></figure></p><p>第二组数据的含义：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">PID   进程id</span><br><span class="line">PR    优先级</span><br><span class="line">CPU%  当前瞬时CPU占用率</span><br><span class="line">S     进程状态:D=不可中断的睡眠状态, R=运行, S=睡眠, T=跟踪/停止, Z=僵尸进程</span><br><span class="line"><span class="comment">#THR  程序当前所用的线程数</span></span><br><span class="line">VSS   Virtual Set Size  虚拟耗用内存（包含共享库占用的内存）</span><br><span class="line">RSS   Resident Set Size 实际使用物理内存（包含共享库占用的内存）</span><br><span class="line">PCY   调度策略优先级，SP_BACKGROUND/SP_FOREGROUND</span><br><span class="line">UID   进程所有者的用户id</span><br><span class="line">Name  进程的名称</span><br><span class="line">PID   进程id</span><br><span class="line">PR    优先级</span><br><span class="line">CPU%  当前瞬时CPU占用率</span><br><span class="line">S     进程状态:D=不可中断的睡眠状态, R=运行, S=睡眠, T=跟踪/停止, Z=僵尸进程</span><br><span class="line"><span class="comment">#THR  程序当前所用的线程数</span></span><br><span class="line">VSS   Virtual Set Size  虚拟耗用内存（包含共享库占用的内存）</span><br><span class="line">RSS   Resident Set Size 实际使用物理内存（包含共享库占用的内存）</span><br><span class="line">PCY   调度策略优先级，SP_BACKGROUND/SP_FOREGROUND</span><br><span class="line">UID   进程所有者的用户id</span><br><span class="line">Name  进程的名称</span><br></pre></td></tr></table></figure></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">adb shell procrank 查询各进程内存使用情况</span><br><span class="line">adb shell service list 查看services信息</span><br><span class="line">adb shell cat /proc/meminfo 查看当前的内存情况</span><br><span class="line">adb shell cat /proc/cpuinfo 查看CPU信息（硬件）</span><br><span class="line">adb shell cat /proc/iomem  查看IO内存分区</span><br></pre></td></tr></table></figure><h1 id="prop属性相关"><a href="#prop属性相关" class="headerlink" title="prop属性相关"></a>prop属性相关</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">查看build.prop文件中变量值 adb shell getprop xxx</span><br><span class="line">adb shell getprop 列出系统所有属性</span><br><span class="line">adb shell getprop | findstr <span class="string">"gsm"</span> 列出包含gsm的属性</span><br><span class="line">adb shell setprop &lt;key&gt; &lt;value&gt;  修改系统属性</span><br></pre></td></tr></table></figure><h1 id="查看数据库信息"><a href="#查看数据库信息" class="headerlink" title="查看数据库信息"></a>查看数据库信息</h1><p>adb shell sqlite3 可以执行sql语句查看数据库信息<br>sqlite3 /data/data/com.android.providers.settings/databases/settings.db<br>查询某个数据库中的所有表<br>sqlite&gt; .tables<br>sqlite&gt; .help 可以查看帮助<br><a href="https://blog.csdn.net/liubingzhao/article/details/47041735" target="_blank" rel="noopener">在adb shell中直接使用sqlite3命令操作数据库</a></p><h1 id="adb-shell-netstat-网络相关"><a href="#adb-shell-netstat-网络相关" class="headerlink" title="adb shell netstat 网络相关"></a>adb shell netstat 网络相关</h1><p>adb shell netstat //查看设备的端口号<br>adb shell netcfg  //查看设备的IP地址</p><p>netstat -ano 查看网络连状态<br>显示协议统计信息和当前 TCP/IP 网络连接。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">NETSTAT \[-a\] \[-b\] \[-e\] \[-n\] \[-o\] \[-p proto\] \[-r\] \[-s\] \[-v\] \[interval\]</span><br><span class="line">``` </span><br><span class="line">``` bash</span><br><span class="line">-a 显示所有连接和监听端口。  </span><br><span class="line">-b 显示包含于创建每个连接或监听端口的可执行组件。在某些情况下已知可执行组件拥有多个独立组件，并且在这些情况下包含于创建连接或监听端口的组件序列被显示。这种情况下，可执行组件名  </span><br><span class="line">在底部的 \[\] 中，顶部是其调用的组件，等等，直到 TCP/IP 部分。注意此选项 可能需要很长时间，如果没有足够权限可能失败。  </span><br><span class="line">-e 显示以太网统计信息。此选项可以与 -s  选项组合使用。  </span><br><span class="line">-n 以数字形式显示地址和端口号。  </span><br><span class="line">-o 显示与每个连接相关的所属进程 ID。  </span><br><span class="line">-p proto 显示 proto 指定的协议的连接；proto 可以是  </span><br><span class="line">下列协议之一: TCP、UDP、TCPv6 或 UDPv6。  </span><br><span class="line">如果与 -s 选项一起使用以显示按协议统计信息，proto 可以是下列协议之一:  </span><br><span class="line">IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 或 UDPv6。  </span><br><span class="line">-r 显示路由表。  </span><br><span class="line">-s 显示按协议统计信息。默认地，显示 IP、  </span><br><span class="line">IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 和 UDPv6 的统计信息；  </span><br><span class="line">-p 选项用于指定默认情况的子集。  </span><br><span class="line">-v 与 -b 选项一起使用时将显示包含于  </span><br><span class="line">为所有可执行组件创建连接或监听端口的  </span><br><span class="line">组件。  </span><br><span class="line">interval 重新显示选定统计信息，每次显示之间  </span><br><span class="line">暂停时间间隔(以秒计)。按 CTRL+C 停止重新  </span><br><span class="line">显示统计信息。如果省略，netstat 显示当前  </span><br><span class="line">配置信息(只显示一次)</span><br></pre></td></tr></table></figure></p><h1 id="adb-shell-pm-包相关"><a href="#adb-shell-pm-包相关" class="headerlink" title="adb shell pm 包相关"></a>adb shell pm 包相关</h1><p><a href="https://blog.csdn.net/yelangjueqi/article/details/52575233" target="_blank" rel="noopener">https://blog.csdn.net/yelangjueqi/article/details/52575233</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">usage: pm \[list|path|install|uninstall\]  </span><br><span class="line">pm list packages \[-f\]  </span><br><span class="line">pm list permission-groups  </span><br><span class="line">pm list permissions \[-g\] \[-f\] \[-d\] \[-u\] \[GROUP\]  </span><br><span class="line">pm list instrumentation \[-f\] \[TARGET-PACKAGE\]  </span><br><span class="line">pm list features  </span><br><span class="line">pm path PACKAGE  </span><br><span class="line">pm dump PACKAGE</span><br><span class="line">pm install \[-l\] \[-r\] \[-t\] \[-i INSTALLER\_PACKAGE\_NAME\] PATH  </span><br><span class="line">pm uninstall \[-k\] PACKAGE  </span><br><span class="line">pm <span class="built_in">enable</span> PACKAGE\_OR\_COMPONENT  </span><br><span class="line">pm <span class="built_in">disable</span> PACKAGE\_OR\_COMPONENT</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">``` bash</span><br><span class="line">The list packages <span class="built_in">command</span> prints all packages. Options:  </span><br><span class="line">-f: see their associated file.</span><br><span class="line"></span><br><span class="line">The list permission-groups <span class="built_in">command</span> prints all known  </span><br><span class="line">permission groups.</span><br><span class="line"></span><br><span class="line">The list permissions <span class="built_in">command</span> prints all known  </span><br><span class="line">permissions, optionally only those <span class="keyword">in</span> GROUP. Options:  </span><br><span class="line">-g: organize by group.  </span><br><span class="line">-f: <span class="built_in">print</span> all information.  </span><br><span class="line">-s: short summary.  </span><br><span class="line">-d: only list dangerous permissions.  </span><br><span class="line">-u: list only the permissions users will see.</span><br><span class="line"></span><br><span class="line">The list instrumentation <span class="built_in">command</span> prints all instrumentations,  </span><br><span class="line">or only those that target a specified package. Options:  </span><br><span class="line">-f: see their associated file.</span><br><span class="line"></span><br><span class="line">The list features <span class="built_in">command</span> prints all features of the system.</span><br><span class="line"></span><br><span class="line">The path <span class="built_in">command</span> prints the path to the .apk of a package.</span><br><span class="line"></span><br><span class="line">The install <span class="built_in">command</span> installs a package to the system. Options:  </span><br><span class="line">-l: install the package with FORWARD_LOCK.  </span><br><span class="line">-r: reinstall an exisiting app, keeping its data.  </span><br><span class="line">-t: allow <span class="built_in">test</span> .apks to be installed.  </span><br><span class="line">-i: specify the installer package name.</span><br><span class="line"></span><br><span class="line">The uninstall <span class="built_in">command</span> removes a package from the system. Options:  </span><br><span class="line">-k: keep the data and cache directories around.  </span><br><span class="line">after the package removal.</span><br><span class="line"></span><br><span class="line">The <span class="built_in">enable</span> and <span class="built_in">disable</span> commands change the enabled state of  </span><br><span class="line">a given package or component (written as “package/class”).</span><br></pre></td></tr></table></figure><p>查看stdout 和stderr<br>在默认状态下，Android系统有stdout 和 stderr (System.out和System.err )输出到/dev/null ，<br>在运行Dalvik VM的进程中，有一个系统可以备份日志文件。在这种情况下，系统会用stdout 和stderr 和优先级 I.来记录日志信息</p><p>通过这种方法指定输出的路径，停止运行的模拟器/设备，然后通过用setprop 命令远程输入日志</p><p>adbshellstop adb shell stop adb shell setprop log.redirect-stdio true<br>$ adb shell start系统直到你关闭模拟器/设备前设置会一直保留，可以通过添加/data/local.prop 可以使用模拟器/设备上的默认设置</p><p>UI/软件 试验程序 Monkey<br>当Monkey程序在模拟器或设备运行的时候，如果用户出发了比如点击，触摸，手势或一些系统级别的事件的时候，<br>它就会产生随机脉冲，所以可以用Monkey用随机重复的方法去负荷测试你开发的软件.<br>最简单的方法就是用用下面的命令来使用Monkey，这个命令将会启动你的软件并且触发500个事件.</p><h1 id="adb-shell-am-相关"><a href="#adb-shell-am-相关" class="headerlink" title="adb shell am 相关"></a>adb shell am 相关</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">adb shell am start ...</span><br><span class="line">adb shell am startservice ...</span><br><span class="line">adb shell am broadcast ...</span><br></pre></td></tr></table></figure><h1 id="adb-shel-monkey-Monkey相关"><a href="#adb-shel-monkey-Monkey相关" class="headerlink" title="adb shel monkey Monkey相关"></a>adb shel monkey Monkey相关</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ adb shell monkey -v -p your.package.name 500  </span><br><span class="line">跑MonKey</span><br><span class="line">adb shell monkey -v -p com.android.settings --ignore-crashes --ignore-timeouts --ignore-security-exceptions --monitor-native-crashes --ignore-native-crashes 10000000 &gt; monkey-log.txt</span><br><span class="line"></span><br><span class="line">adb shell monkey -v -p com.android.settings --ignore-crashes --ignore-timeouts --ignore-security-exceptions --monitor-native-crashes --ignore-native-crashes --throttle 100 -v 10000000 &gt; monkey-log.txt</span><br></pre></td></tr></table></figure><p>更多的关于命令Monkey的命令的信息，可以查看UI/Application Exerciser Monkey documentation page.</p><h1 id="adb-shell-ps-系列"><a href="#adb-shell-ps-系列" class="headerlink" title="adb shell ps 系列"></a>adb shell ps 系列</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ps -e | grep -rn systemui</span><br><span class="line">ps -e | grep -rn system_server</span><br></pre></td></tr></table></figure><h1 id="adb-可用的命令"><a href="#adb-可用的命令" class="headerlink" title="adb 可用的命令"></a>adb 可用的命令</h1><p> 在system/bin下面的所有的bin文件，几乎都可以使用adb bin文件名的方式来调用，应该说是大部分吧，有些还不能使用<br> adb shell进入后，执行如下命令<br> atrace：</p><p># </p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;!--
修改记录：
1.2016年完成第一版
2.2018年修订
完成目标：分析adb的源码
    --&gt;
&lt;p&gt;ADB (Android Debug Bridge)&lt;/p&gt;
&lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android ADB" scheme="http://yoursite.com/tags/Android-ADB/"/>
    
  </entry>
  
  <entry>
    <title>ActionBar隐藏方法</title>
    <link href="http://yoursite.com/ActionBar%E9%9A%90%E8%97%8F%E6%96%B9%E6%B3%95/"/>
    <id>http://yoursite.com/ActionBar隐藏方法/</id>
    <published>2015-06-29T03:26:48.000Z</published>
    <updated>2018-08-07T07:07:06.066Z</updated>
    
    <content type="html"><![CDATA[<p>当使用Android中的ActionBar控件时，如果想要隐藏上面的ActionBar，可以使用如下的代码：<br>getSupportActionBar().hide();//隐藏掉整个ActionBar，包括下面的Tabs<br>上面的代码会将整个ActionBar都隐藏掉，包括ActionBar中的Tab分页标签，如果想要保留分页标签的话，可以使用如下的代码：<br>ActionBar actionBar = getSupportActionBar();//高版本可以换成</p><p>ActionBar actionBar = getActionBar();<br>actionBar.setDisplayShowTitleEnabled(false);<br>actionBar.setDisplayShowHomeEnabled(false);<br>//会保留tab标签</p><p>另外还有一种更简单的方式来移除ActionBar，在setContent()之前调用下面这句，保证没有ActionBar<br>导入包：<br>import android.view.Window;<br>requestWindowFeature(Window.FEATURE_NO_TITLE);</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;当使用Android中的ActionBar控件时，如果想要隐藏上面的ActionBar，可以使用如下的代码：&lt;br&gt;getSupportActionBar().hide();//隐藏掉整个ActionBar，包括下面的Tabs&lt;br&gt;上面的代码会将整个ActionBar都隐
      
    
    </summary>
    
      <category term="Android Tools" scheme="http://yoursite.com/categories/Android-Tools/"/>
    
    
      <category term="Android ActionBar" scheme="http://yoursite.com/tags/Android-ActionBar/"/>
    
  </entry>
  
  <entry>
    <title>Android</title>
    <link href="http://yoursite.com/year-06-23-Android/"/>
    <id>http://yoursite.com/year-06-23-Android/</id>
    <published>2015-06-23T12:26:02.000Z</published>
    <updated>2018-08-07T07:59:34.293Z</updated>
    
    <content type="html"><![CDATA[<p></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;/p&gt;

      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Hexo" scheme="http://yoursite.com/tags/Hexo/"/>
    
  </entry>
  
  <entry>
    <title>Android面试汇总</title>
    <link href="http://yoursite.com/Android-InterView/"/>
    <id>http://yoursite.com/Android-InterView/</id>
    <published>2015-01-31T16:00:00.000Z</published>
    <updated>2018-08-31T07:09:05.874Z</updated>
    
    <content type="html"><![CDATA[<!--# 自己简历上写的技能：1.对Android非常感兴趣，对移动互联网充满激情，愿意未来二十年从事移动互联网行业;2.Android SDK以及Android各组件应用、JNI开发、多线程、线程池的使用;3.熟悉Android的SystemUI/Keyguard/Launcher三个模块;4.熟练掌握Git/SVN/Android Studio/Eclipse/ADT/MyEclipse/VisualStudio等开发工具;5.了解Android系统架构，具有解读Android源码的能力;6.了解Android的Framework系统框架，AIDL，Binder和多进程通信(Socket)，多线程通讯，Handler(Message/MessagerQueue/Looper),View的绘制原理，滑动冲突，View的measure/layout/draw以及事件传递机制，动画;7.了解Android的Framework中AMS/WMS/PMS8.了解SeLinux，JNI相关知识;9.Android性能优化LMK,卡顿,省电等10.Android稳定性,ANR,Crash,Tombstone,Freeze,Panic,OOM问题的分析11.了解设计模式12.了解图像处理算法;自己也要能够逐条的进行剖析，以及解答.--><p>APP –&gt; Framework –&gt; 底软<br>学习 –&gt; 使用 –&gt; 实现原理 –&gt; 形成自己的理解 –&gt; 给自己留下烙印</p><p>A:基础<br>通过关键字来梳理这些相关的知识点</p><ol><li>开关机流程</li></ol><ol start="2"><li><p>ActivityManagerService<br>Activity启动流程, Instrumentation类提供的各种流程控制方法<br>ActivityStack<br>ActivityThread</p></li><li><p>PackageManagerService</p></li><li><p>WindowManagerService</p></li><li><p>进程通讯IPC的方式:<br>Linux中有pipe管道，命名管道FIFO，内存映射(mapped memeory)，消息队列(message queue)，内存共享(shared memeory)，信号量(semaphore)，信号(signal)，套接字(socket) 等八种方式.<br>Binder<br>Serializable 和 Parcelable 进行序列化<br>AIDL<br>Messager<br>Android中IPC的方式：<br>1.Bundle<br>2.文件共享<br>3.Messenger<br>4.AIDL<br>5.ContentProvider<br>6.Socket</p></li></ol><ol start="6"><li>JNI</li></ol><ol start="7"><li><p>View<br>View的工作原理，Event事件<br>measure 测绘  测量View的宽和高<br>layout 布局   确定View最终宽和高，以及四个角的顶点<br>draw 绘制  </p></li><li><p>SeLinux</p></li></ol><ol start="9"><li>ANR</li><li>Crash</li><li>Tombstone</li><li>Freeze</li><li>OOM</li><li>LMK </li><li>卡顿</li><li>省电</li><li>堆和栈<br>堆内存和栈内存的概念<br>栈内存：系统自动分配和释放，<br>保存全局、静态、局部变量，<br>在站上分配内存叫静态分配，<br>大小一般是固定的<br>堆内存：程序员手动分配(malloc/new)和释放(free/java不用手动释放，由GC回收)，<br>在堆上分配内存叫动态分配，<br>一般硬件内存有多大堆内存就有多大</li></ol><p><a href="https://blog.csdn.net/gyhgx/article/details/70161248" target="_blank" rel="noopener">一些底层基本知识（Android篇二）</a></p><ol start="18"><li>Android的线程和线程池<br>AsyncTask：封装了线程池和Handler，主要是为了方便开发者在子线程中更新UI<br>IntentService<br>HandlerThread<br>线程池</li></ol><p>底软模块：</p><p>B:做过哪些工作</p><p>功耗、性能、稳定性三大块</p><p>性能优化：</p><p><a href="https://blog.csdn.net/csdn_aiyang/article/details/74989318" target="_blank" rel="noopener">Android 性能优化之内存检测、卡顿优化、耗电优化、APK瘦身</a><br><a href="https://www.jianshu.com/p/f7006ab64da7" target="_blank" rel="noopener">Android性能优化系列</a></p><p>一些有效的性能优化方法：布局优化、绘制优化、内存泄漏优化、响应速度优化、ListView优化、Bitmap优化、线程优化。<br>布局优化：尽量减少布局的层级<br>首先删除布局中无用的控件和层级，其次有选择地使用性能较低的ViewGroup，比如RelativeLayout。尽量使用性能较高的ViewGroup如LinearLayout。<br>另一种手段就是采用标签、标签和ViewStub.标签主要用于布局重用，标签和标签配合使用，降低减少布局的层级。而ViewStub则提供了按需加载功能，提高了程序的初始化效率。<br>绘制优化：<br>绘制优化是指View的onDraw方法要避免执行大量的操作。主要体现在两个方面： </p><ol><li>onDraw中不要创建新的局部对象。 </li><li>onDraw中不要做耗时的任务。<br>内存泄漏优化：<br><a href="http://zmywly8866.github.io/2016/05/04/android-application-leak-analysis-and-fix.html" target="_blank" rel="noopener">Android应用内存泄露分析、改善经验总结</a></li></ol><p>响应速度优化：<br>ListView优化：<br>Bitmap优化：<br>线程优化：</p><p>性能优化用到的工具<br>Android Studio的 Analyze-Inspect Code 对代码做静态分析，查找常见的内存泄漏问题<br><a href="https://blog.csdn.net/qq_17265737/article/details/77337207" target="_blank" rel="noopener">Android Sutdio自带的代码检查工具analyze的使用</a></p><p>代码检查的工具：</p><p>性能测试工具：<br>1.APP启动时间<br><a href="https://www.jianshu.com/p/eb82b321a0c9" target="_blank" rel="noopener">Android性能测试 | 启动时间篇</a><br><a href="https://blog.csdn.net/ylyg050518/article/details/80383478" target="_blank" rel="noopener">Android性能优化——优化应用启动时间</a> Google官网的翻译<br><a href="https://blog.csdn.net/zhangbijun1230/article/details/79590345" target="_blank" rel="noopener">Android 性能优化—（8）APP启动时间优化指南</a></p><p>思路： 启动方式/原理 –&gt; 查看启动时间的方式 –&gt; 查看启动时间的工具 –&gt; 启动时间的解决方案</p><p>APP启动方式：<br>a).冷启动：需要新建用户进程<br>冷启动时，系统需要执行三个任务<br>  1.加载和启动应用程序,<br>  2.在app启动后立即显示一个空白启动窗体<br>  3.创建App进程。<br>一旦系统创建了应用进程，应用进程就会执行下面步骤：<br>  1.创建应用程序对象<br>  2.启动主线程<br>  3.创建Main Activity<br>  4.初始化构造view<br>  5.在屏幕上布局。<br>  6.执行初始化绘制操作<br>b).热启动：<br>c).暖启动：</p><p>查看启动时间的方式：<br>a) 通过log中的Displayed字段查找<br>b) adb shell am start -S -W com.example.app/.MainActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN<br>比如：adb shell am start -S -W com.android.systemui/.recents.RecentsActivity<br>c) 通过reportFullyDrawn函数确定完全显示耗时—针对应用有延长的情况比较有用<br>d)</p><p>启动时间查看工具：<br>a) Trace函数以及Systrace工具<br>b) AndroidStudio 中的 Method Tracer作用就是监听一段时间内，某个进程的某个线程，执行的所有方法，以及各个方法所消耗的时间 <a href="https://segmentfault.com/a/1190000011084104" target="_blank" rel="noopener">在Android Studio中使用Method trace,查看某进程的所有线程trace的方法</a></p><p>APP启动时间实例：<br>a)初始化开销大<br>Application 的创建过程中，如果执行复杂的逻辑或者初始化大量的对象，将会影响应用的启动体验<br>解决方案:</p><p>2.开机启动时间</p><p><a href="https://blog.csdn.net/zhangbijun1230/article/details/80562616" target="_blank" rel="noopener">Android 系统性能优化（58）—开机时间优化</a><br><a href="https://blog.csdn.net/ljp1205/article/details/78360701" target="_blank" rel="noopener">Android O 启动优化</a><br><a href="https://blog.csdn.net/u010164190/article/details/51463492" target="_blank" rel="noopener">Android 开机速度优化—–ART 预先优化</a></p><p><a href="https://blog.csdn.net/shirakawakanaki/article/details/53516325" target="_blank" rel="noopener">Android性能优化之路（一）</a><br>检测卡顿工具：<br>a)TraceView<br>b)MethodProfiling<br>c)Profile GPURendering(GPU显示配置文件)  在开发者工具里面有开关可以打开，</p><p><a href="https://www.aliyun.com/jiaocheng/2368.html" target="_blank" rel="noopener">Android系统的启动时间</a></p><p>3.内存<br>思路： 内存查看 –&gt; 内存泄漏与OOM的常见方式  –&gt; 检测工具  –&gt; 如何优化  –&gt; 自己的实战情况</p><p><a href="https://blog.csdn.net/heshushun/article/details/77893817" target="_blank" rel="noopener">Android客户端性能测试—内存（一）</a><br>android程序内存被分为2部分：native和dalvik，dalvik就是我们平常说的java堆，我们创建的对象是在这里面分配的，而bitmap是直接在native上分配的，对于内存的限制是 native+dalvik 不能超过最大限制。android程序内存一般限制在16M，当然也有24M的</p><p>两条命令查看内存：<br>a) adb shell dumpsys meminfo PID/包名  -d<br>在进入一个界面之前查看一遍Activity和View的数量，在退出这个界面之后再查看一遍Activity和View的数量，对比进入前和进入后Activity和View数量的变化情况，如果有差异，则说明存在内存泄露（在使用命令查看Activity和View的数量之前，记得手动触发GC）。<br>手动触发GC的方式：1.DDMS中有个按钮  2.代码中调用System.gc(); 3.当创建一个内存分析文件HPROF时触发,使用adb shell am dumpheap可以触发分析HPROF文件</p><p>b）adb shell procrank<br>Procrank可以同时获得以下几种内存的信息： VSS：Virtual Set Size虚拟耗用内存（包含共享库占用的内存）。This size also includes memory that may not be resident in RAM like mallocs that have been allocated but not written to，所以用VSS来衡量一个进程实际使用的内存意义不大。</p><p>RSS : Resident Set Size实际使用物理内存（包含共享库占用的内存）。 RSS can be misleading, because it reports the total all of the shared libraries that the process uses, even though a shared library is only loaded into memory once regardless of how many processes use，所以用RSS来衡量进程占用的内存信息不是特别准确。</p><p>PSS - Proportional Set Size实际使用的物理内存（比例分配共享库占用的内存）。 PSS is a very useful number because when the PSS for all processes in the system are summed together, that is a good representation for the total memory usage in the system，所以用pss衡量程序占用的内存误差比较小。</p><p>USS - Unique Set Size进程独自占用的物理内存（不包含共享库占用的内存）. USS is an extremely useful number because it indicates the true incremental cost of running a particular process，所以用USS描述进程占用内存的波动和峰值比较有意义。</p><p>一般存在以下关系VSS&gt;=RSS&gt;=PSS&gt;=USS。<br>c) adb shell getprop|grep heapgrowthlimit  查看最大内存<br>d) adb shell top  列出top*进程的cpu和内存占用情况，默认按照cpu占用降序排列。top可以获得进程的VSS和RSS信息，命令持续的监视</p><p>内存泄漏和OOM内存溢出<br>通过命令或者工具，如果发现内存一直在增长，说明发生了内存泄漏.<br>OOM内存溢出</p><p>常见的内存泄漏，可以用代码静态扫描找出来.<br>常见的代码静态扫描工具：<br><a href="https://www.jianshu.com/p/5834c56a5105" target="_blank" rel="noopener">Android 静态代码扫描流程及工具说明</a><br>1.Android Lint<br>2.Godeyes<br>3.Infer</p><p>常见的代码扫描案例：<br>[空指针]空指针引用<br>[内存泄露]Stream资源关闭<br>[性能]使用indexOf(字符)<br>[兼容]系统API兼容性隐患<br>[越界]数组下标越界隐患<br>[异常] 使用除法或求余没有判断分母长度隐患<br>[SQL]注入风险<br>[应用安全] AndroidMannifest.xml文件中allowBackup设置为true时会导致数据泄露</p><p>常见的内存泄漏：<br>1.IO操作后，没有关闭文件导致的内存泄露，比如Cursor、FileInputStream、FileOutputStream使用完后没有关闭<br>2.自定义View中使用TypedArray后，没有recycle<br>3.某些地方使用了四大组件的context，在离开这些组件后仍然持有其context导致的内存泄露，这种问题属于共识，在编写代码的过程中就应该按照规则来，使用Application的Context就可以解决这类内存泄露的问题了</p><p>分析内存泄漏的工具<br>1.MAT  <a href="https://blog.csdn.net/xiaanming/article/details/42396507" target="_blank" rel="noopener">Android 性能优化之使用MAT分析内存泄露问题</a><br>2.Android Studio的 Analyze-Inspect Code对代码做静态分析<br>3.Leakcanary</p><p>内存泄漏优化：<br><a href="http://zmywly8866.github.io/2016/05/04/android-application-leak-analysis-and-fix.html" target="_blank" rel="noopener">Android应用内存泄露分析、改善经验总结</a><br><a href="https://www.jianshu.com/p/f37f4e1e9500" target="_blank" rel="noopener">Android 内存泄露优化处理</a></p><p>3.CPU<br>安卓性能指标cpu主要关注两点：<br>（1）cpu总体使用率（2）应用程序cpu占用率。</p><p>CPU利用率：CPU执行非系统空闲进程的时间 / CPU总的执行时间。<br>Android关于进程使用率的限制：前台进程不超过95%,后台进程5%, 但是在系统没有前台进程时，后台进程可以超过5%。<br>cpu使用过高，可能引发的问题<br>1）整体性能降低<br>2）界面卡顿<br>3）响应慢，容易引起ANR<br>4）手机发热<br>CPU测试项<br>1.空闲状态下的应用CPU消耗情况<br>2.中等规格状态下的应用CPU消耗情况<br>3.满规格状态下的应用CPU消耗情况<br>4.应用CPU峰值情况<br>查看方式</p><ol><li>adb shell  dumpsys cpuinfo|grep ‘<package name="">‘<br>2.安卓sdk中tools下找到ddms，启动查看<br><a href="https://www.jianshu.com/p/edeabdb0c60a" target="_blank" rel="noopener">Android性能测试之cpu占用率</a><br><a href="https://blog.csdn.net/KfcCola/article/details/70597500" target="_blank" rel="noopener">Android CPU性能文件位置</a><br><a href="https://blog.csdn.net/heshushun/article/details/77932879" target="_blank" rel="noopener">Android客户端性能测试—CPU、启动时间（二）</a><br>上面的三篇文章都提到了使用adb相关的命令来查看CPU的情况<br>adb shell echo 3&gt;/proc/sys/vm/drop_caches 可以清除系统cache</package></li></ol><p><a href="https://www.jianshu.com/p/6bf564f7cdf0" target="_blank" rel="noopener">安卓性能测试之cpu占用率统计方法总结</a></p><p>设置CPU核心数和频率<br><a href="https://www.jianshu.com/p/111f79ab2106" target="_blank" rel="noopener">Android下设置CPU核心数和频率</a>  通过变化online的核心数和调整CPU频率可以做到功耗和性能的特殊要求。<br><a href="https://bbs.htc.com/cn/chat.php?mod=viewthread&amp;tid=88398&amp;extra=&amp;page=1" target="_blank" rel="noopener">分享一个最全的Android的CPU各种模式设置及作用</a></p><p>设置I/O调度<br><a href="https://www.jianshu.com/p/f32354f2c6fc" target="_blank" rel="noopener">Android手机I/O调度模式简介</a><br><a href="https://bbs.htc.com/cn/chat.php?mod=viewthread&amp;tid=88400&amp;extra=" target="_blank" rel="noopener">分享一个最全的Android的I/O调度各种模式设置及作用</a></p><p>解决CPU使用率高的实例？<br><a href="https://blog.csdn.net/qq_28607155/article/details/68927354" target="_blank" rel="noopener">Android CPU使用过大的问题解决以及造成的原因</a></p><p><a href="https://www.jianshu.com/p/eb82b321a0c9" target="_blank" rel="noopener">Android性能测试|流量、电量、弱网环境的测试方法</a></p><p>4.耗电量<br>APP耗电的原因：<br>1.http请求(GZIP压缩)<br>2.json数据解析(json解析效率主要是解析耗时)，大量的数据解析<br>3.数据库读写操作<br>4.SD卡读写操作<br>5.程序的执行的timer定时器（例如IM中的心跳包，用系统的Alarm优化）<br>6.网络切换（wifi会比手机的数据移动网络更加省电）<br>针对以上几种原因的耗电，分别处理<br><a href="https://blog.csdn.net/lhd201006/article/details/50546847" target="_blank" rel="noopener">Android App耗电分析</a></p><p>系统耗电的原因：<br>1.蓝牙耗电<br>2.wifi耗电<br>3.移动数据耗电<br>4.CPU耗电<br>5.摄像头耗电<br>6.GPS耗电<br>7.手电筒耗电<br>8.wakelock耗电<br>9.各种Sensor耗电<br><a href="https://blog.csdn.net/itfootball/article/details/49155979" target="_blank" rel="noopener">Android性能专项测试之耗电量统计API</a></p><p>耗电分析工具：<br>1.BatteryHistorian 由Google提供的Android系统电量分析工具,从手机中导出bugreport文件上传至页面,在网页中生成详细的图表数据来展示手机上各模块电量消耗过程,最后通过App数据的分析制定出相关的电量优化的方法。<br><a href="https://www.aliyun.com/jiaocheng/35531.html" target="_blank" rel="noopener">Android应用耗电量分析与优化建议</a><br><a href="https://blog.csdn.net/itheimach/article/details/70545139" target="_blank" rel="noopener">电量优化的工具battery-historien</a><br><a href="https://blog.csdn.net/zhaoshuyu111/article/details/52942161" target="_blank" rel="noopener">Android应用耗电问题排查</a></p><p>5.流量<br>获取流量信息的方法:<br><a href="https://blog.csdn.net/u013948858/article/details/76079367" target="_blank" rel="noopener">【Android】性能测试之获取Android流量数据</a><br><a href="https://testerhome.com/topics/2643" target="_blank" rel="noopener">Android 性能测试实践 (四) 流量</a></p><p>网络的优化<br><a href="https://www.jianshu.com/p/d4c2c62ffc35" target="_blank" rel="noopener">Android App优化之网络优化</a><br><a href="https://developer.android.google.cn/training/basics/network-ops/data-saver?hl=zh-cn" target="_blank" rel="noopener">优化网络流量消耗</a> 来自Android官网</p><p>弱网优化</p><p>网络优化工具：<br>1.tcpdump抓包<br>2.Wireshark抓包<br><a href="https://blog.csdn.net/java2013liu/article/details/53611913" target="_blank" rel="noopener">Android的app性能测试–流量</a></p><p>6.流畅度<br>又叫FPS，或者刷新率VS帧率<br>刷新率：每秒屏幕刷新次数，手机屏幕的刷新率是60HZ<br>帧率：GPU在一秒内绘制的帧数<br>撕裂<br>因为屏幕的刷新过程是自上而下、自左向右的，如果帧率&gt;刷新率，当屏幕还没有刷新n-1帧的数据时，就开始生成第n帧的数据了，从上到下，覆盖第n-1帧。如果此时刷新屏幕，就会出现图像的上半部分是第n帧的，下半部分是第n帧的现象。CPU/GPU一直都在渲染。<br>丢帧<br>Android系统每隔16ms发出VSYNC信号，触发GPU对UI进行渲染，如果你的某个操作花费时间是24ms，系统在得到VSYNC信号的时候由于还没有准备好，就无法进行更新任何内容，那么用户在32ms内看到的会是同一帧画面（卡顿现象），即丢帧现象。<br><a href="https://www.cnblogs.com/cloudiest/p/8485711.html" target="_blank" rel="noopener">Android App性能评测分析－流畅度篇</a></p><p><a href="https://blog.csdn.net/weixin_37730482/article/details/73810179" target="_blank" rel="noopener">Android流畅度总结</a></p><p>稳定性：<br>1.ANR<br>2.Crash<br>3.Tombstone<br>4.Freeze<br>5.黑屏<br>6.冻屏</p><p>功耗：</p><!--15薪是写在工资里面的，有绩效，每个月有1000左右的补贴# 关于传音的招聘##职位一：android开发（Framework）18-30万、包含年底双薪职责描述：1.紧跟行业水平，负责功耗相关标准的制定2.负责用户实际场景下的功耗优化，解决相关的功耗问题；3.紧跟行业技术，负责Android-Framework功耗优化相关的feature的设计和实现；4.优化系统整体功耗，负责相关创新技术的设计和实现任职要求：1.计算机，通信，电子相关专业本科以上学历，一年以上ANDROID领域APP/Framework开发经验；2.自我驱动，有强烈的责任感和好奇心，良好的团队合作精神，积极主动，善于沟通；3.具备以下领域一项或者多项背景经验皆可a)熟悉ANDROID框架，具有软件架构设计能力，尤其在以下领域之一有产品经验：Activity Manager Service，Package Manager Service，Windows Manager Service，Location Manager Service，Power Manager Service，Battery Status，Alarm Manager，ART/GC，Stagefrightb)具有实际场景下功耗优化经验，如Launhcer，web view，media player等等c)熟悉LINUX-ANDROID平台的调试手段与工具，如Systrace， Perf，Historian，Function trace4.良好的英语读写能力，能快速阅读相关英文资料##职位二：Android 系统安全工程师18-30万职责描述：1、负责Android系统中谷歌安全相关知识的解读和预研。2、负责分析和跟踪手机产品系统安全行业趋势和最新动态。4、负责公司安全策略的执行和监督，确保安全策略落地执行。4、负责对内部研发人员进行系统安全的定期培训和考核。5、负责Android系统安全技术的咨询和问题解决。任职要求：1、本科及以上学历，计算机工程、 计算机科学或相关专业背景；2、2年Android手机开发经验，有Android手机相关安全功能的设计、开发经验者优先3、具TEE/SE理论知识及TEE应用编程经验优先；4、具备加密方法及证书使用相关经验优先；5、了解安全编码方法及SSDLC知识经验优先；6、英语读写流利。 ##职位三：android高级软件开发工程师21-42万职位描述：1 参与产品需求分析，制定技术实施方案；2 负责Android手机应用的架构设计和开发，并确保进度和质量要求；3 负责重点技术问题的技术攻关；4 参与制定特定的开发流程和规范，并指导执行；5 指导并培养普通工程师，提升团队开发水平；必备知识与技能 有丰富的Android手机应用架构设计经验，对Android系统有全面的了解，能带领团队进行功能开发；目前，系统稳定性，多媒体图库，通讯，系统应用等方向均有需求##职位四：高级安卓开发工程师（systemui/framework/multimedia/launcher）高级软件开发工程师工作职责：1 负责带领开发小组或者独立完成软件需求分析、风险评估、软件实现方案及开发计划制定、开发小组成员职责划分、概要设计2 负责带领、协调开发小组或者独立完成代码框架搭建、软件编码，负责开发小组与其他部门的沟通协调3 负责对软件开发工程师和新员工代码质量检查、培训指导4 负责对部分系统问题、疑难问题、性能功耗问题进行攻关、分析、优化和解决5 总结技术文档、经验教训，在部门内部进行培训必备知识与技能 1.  扎实的Java技术功底和android系统架构知识 2.  4年以上android应用软件开发经验，拥有Android系统框架开发经验3.  熟练掌握android开发所有工具的使用，可以独立承担部分疑难问题分析解决4.  具备相对复杂功能的软件架构设计、代码编码实现的能力5.  思路清晰、有很强的技术钻研精神和沟通协调能力，能承受一定的工作压力，具备带领开发小组的能力6.  具备良好的文档编写能力，能熟练阅读英文技术文档##职位五：软件测试工具开发工程师（Android应用开发）18-36万岗位描述：1. 负责Android平台的应用设计、开发和调试；2. 能够独立开发Android应用程序；3、负责改善用户界面的易用性，提升用户体验度；4、维护和优化相关的APP产品和工具的质量/性能；任职资格：1、计算机相关专业，大专以上学历，2年以上安卓开发经验；2、精通基于Android SDK的应用程序开发，能独立进行应用程序开发、调试、适配和移植；3、熟悉Android平台架构、原理、Android SDK，熟悉HTTP、HTTPS、TCP/IP通讯协议； 4、扎实的Java语言基础，熟练使用AndroidSdk进行开发，深入了解Android系统组件，深入理解Android多线程模型； 5、熟练掌握android各种UI布局和控件、网络框架、测试框架、能够熟练自定义UI控件 ；6、有独立解决问题的能力，具备良好的沟通能力和优秀的团队协作能力 ；7、有个人作品或者有github作品源码贡献者优先考虑# 关于闻泰的招聘##职位一：Android 软件开发工程师 (System Debug)任职要求:1、本科或以上学历，计算机软件、通信相关专业；2、2年或以上相关工作经验或者Android开发经验；3、熟练掌握C/C++/Java程序设计；4、熟悉Android系统问题的分析和解决，常见问题包括死机/定屏/ANR/Crash/Panic/性能优化等；5、熟悉Android Framework 基本框架，重要服务；6、掌握Linux操作系统内存管理，进程管理/调度等基本知识；7、有ARM基础，汇编语言基础者优先；8、有Qualcomm 平台开发经验者优先；9、工作认真细致,主动性强,责任心强,有较高独立工作能力, 能承受工作压力。岗位职责：1、技术领域：Statility(Crash/Reboot/Shutdown/Hang),Security(keystore,Selinux,SeAndroid,FRP,Boot verify,FDE,ROOT Check,TEE(QSEE) ) 需求实现/问题解决；2、技术开发：所负责技术领域的功能/需求开发，文档撰写，评审；项目的BSP Bring up, 项目各个阶段的Debug；3、技术攻关：参与System领域的技术攻关；4、技术支持：负责System领域的技术培训；5、技术积累：System领域的经验库总结，问题分析报告，问题回溯报告, 工具使用指导，SOP梳理；6、知识产权：专利以及软件著作权申请。##职位二：Android软件开发工程师职要求： 1、本科以上学历 ，计算机或通信相关专业； 2、精通java/c ,熟悉linux操作系统； 3、熟悉android软件架构； 4、熟悉android framework； 3、熟练androidui 开发以及特效处理； 3、掌握java语言和xml解析数据； 4、掌握android进程间通信方法； 5、掌握socket和http网络传输，熟悉xmpp协议、json数据解析； 6、富有团队和敬业精神，工作主动，有较强的责任感和严谨的工作作风； 7、有良好的文档撰写能力，英文阅读能力。 有以下任一经验者优先： 1、熟悉图形图像算法 ；2、熟悉常见的编码格式算法、如二维码等； 3、熟悉数据同步协议 ；4、熟悉android下网络通信机制，对sockdet通信、tcp/ip和http有较深刻的理解和经验，有网络编程经验 ；5、掌握手机安全机制，熟悉攻防技术以及安全漏洞原理。 职位描述： 1、引入新技术：熟悉市场上出现的和应用，及时在软件中引入 ；2、软件开发：新功能和新ui的设计和开发； 3、软件集成：将新功能和新技术的开发成果引入项目中； 4、软件维护：维护代码，使代码保持一定的兼容性； 5、技术支持：负责提供软件相关问题的技术培训 ；6、软件升级：收集软件使用反馈意见，修正缺陷或故障，设计升级版本，提高软件的使用性能； 7、知识产权管理：协助专利以及软件著作权申请，确保公司利益。# 关于展讯的招聘##职位一：Android 应用软件工程师0943岗位职责:1、负责展讯平台 Android 应用客户问题解决处理；2、能够对接客户完成模块相关的客户支持工作；3、负责编写相关的技术文档以及流程梳理。任职资格:1、熟悉Android系统结构；2、有Android系统应用（SystemUI/Launcher/MultiMedia/Tele/Contacts等）问题处理、功能需求实现经验；3、对某个Android系统应用或 Android framework(AMS/PMS/WMS/Input等)有深入经验者优先；4、有稳定性、性能问题处理经验者优先；5、能够熟练使用 Android 和 Linux 下各种常见工具；6、能够与团队成员或部门同事合作，清楚地交流信息、思想或观点；7、良好的学习能力和理解能力。##职位二：高级软件工程师0887岗位职责:1. 承担各芯片平台Android Security功能（FBE\FDE\FRP等）开发维护和客制化定制工作；2. 承担各芯片平台Android Security加密框架和加密库（JCA\Boringssl等）的开发维护；3. 承担各芯片平台指纹等生物识别驱动的开发以及配合厂商调试安全方案工作；4. 承担各芯片平台VerifyBoot Dmverity功能开发和维护；5.. 学习新技术，按照公司发展规划和开发新产品。任职资格:1. 计算机软件、通信、电子相关专业本科以上学历，硕士2年、本科3年以上相关工作经验；2. 熟练掌握C/C++/Java语言与软件调试工具，熟练掌握Android Framework开发和调试技能；3. 有JCA、FBE、FDE、Directboot调试和Debug经验优先，有kernel文件系统开发和调试经验优先；4. 有Secure Boot、Dmverity、VerifyBoot开发和调试经验优先；5. 具备TEE开发和CA\TA开发调试经验以及生物识别安全调试经验优先；6. 具备较强的系统分析能力和产品思维，能够快速适应和深入新产品方向，思路清晰，能和客户/协作部门进行良好沟通合作；7. 具有较强的事业心、责任感, 抗压能力和团队协作精神，良好的英文沟通表达和文档总结能力，能够适应短期出差。##职位三：高级软件工程师(System)0902岗位职责:1.基于展锐芯片平台，交付车联网/智能设备新产品软件开发；2.评估技术方案可行性，解决产品形态差异化Feature的问题；3.编写和评审技术文档，保证设计完整性和积累产品技术经验库；4.支持行业客户与大客户技术问题，保证客户顺利量产；5.主要业务方向在Android/Linux平台稳定性分析和系统问题。任职资格:1、计算机软件、通信、电子相关专业本科以上学历，3年以上产品开发工作经验；2、熟练掌握嵌入式编程语言与调试工具，具备软件工程与嵌入式系统软件知识；3、具备较强的系统分析能力，能够快速适应和深入新技术，思路清晰，有良好的沟通协作能力；4、具有较强的责任感, 抗压能力，良好的英文沟通表达和文档总结能力，能够适应出差需求；5、 熟悉Android系统框架/Linux Kernel, 能够对Crash/Panic等定屏死机问题进行系统分析；6、具备展锐芯片平台软件开发，OS系统移植，系统性能调优，项目技术PL经验者更佳。##职位四：软件系统工程师0947岗位职责:1. 在芯片设计阶段与ASIC团队沟通，进行系统软件方案的设计工作；2. 在产品量产阶段与研发团队，产品管理和客户支持团队完成客户需求的分析和软件方案设计；3. 对手机等产品上出现的新技术与硬件等团队合作进行预研和方案设计验证(软件部分)。任职资格:1. 具有本科或以上学历，有三年或以上Android平台或嵌入式产品开发经验；2. 有较强的自主工作能力和团队合作精神；3. 有人工智能或音频/视频系统开发经验优先；4. 熟悉C/C++或Java等开发工具。--><h1 id="Java面试题"><a href="#Java面试题" class="headerlink" title="Java面试题"></a>Java面试题</h1><h2 id="Java基础"><a href="#Java基础" class="headerlink" title="Java基础"></a>Java基础</h2><ol><li>集合类以及集合框架；HashMap与HashTable实现原理，线程安全性，hash冲突以及处理算法，ConcurrentHashMap</li></ol><ol start="2"><li>进程和线程的区别</li></ol><ol start="3"><li><p>Java的并发、多线程、线程模型</p></li><li><p>什么是线程池，如何使用<br>线程池就是事先将多个线程对象放到一个容器中，当使用额时候就不用new线程，而是直接去线程池中拿线程就可以了，节省了开辟子线程的时间，提高代码的执行效率。</p></li><li><p>数据一致性如何保证；Synchronized关键字，类锁，方法锁和重入锁</p></li><li><p>Java中实现多态的机制是什么</p></li><li><p>如何将一个Java对象序列化到文件中</p></li><li><p>说说对Java反射的理解<br>Java中的反射，首先是能够获取到Java中要反射类的字节码，获取字节码有三种方式<br>a)Class.forName(classname)<br>b)类名.class<br>c) this.getClass()<br>然后将字节码中的方法、变量、构造函数等映射成相应的Method、Filed、Constructor等<br>d)同步的方法：多进程开发以及多进程应用场景</p></li><li><p>在Java中wait和sleep方法的不同<br>最大的不同是在等待的时候wait会释放锁，而sleep一直持有锁。wait通常用于线程间的交互，sleep通常用于执行暂停</p></li><li><p>synchronized和volatile关键字的作用<br>1) 保证了不同线程对这个变量进行操作时的可见性，即一个线程修改了某个变量的值，这个新值对其他线程来说是立即可见的</p></li></ol><ol start="11"><li><p>服务器只提供数据接收接口，在多线程或多线程条件下，如何保证数据的有序到达</p></li><li><p>TheadLocal原理，实现以及如何保证Local属性</p></li><li><p>String、StringBuilder、StringBuffer的对比</p></li><li><p>你所知道的设计模式</p></li><li><p>Java如何调用C、C++语言</p></li><li><p>接口和回调，回调的原理，写一个回调的demo</p></li><li><p>泛型的原理，举例说明；解析与分派</p></li><li><p>抽象类和接口的区别；应用场景，抽象类是否可以没有方法和属性</p></li><li><p>静态属性和静态方法是否可以被继承？是否可以被重写？什么原因</p></li><li><p>修改对象A的equals方法的签名，那么使用HashMap存放这个对象实例的时候，会调用哪个equals方法</p></li><li><p>说说对泛型的理解</p></li><li><p>Java的异常体系</p></li><li><p>如何控制某个方法运行并发访问线程的个数</p></li><li><p>动态代理的区别，分别在什么场景下使用</p></li></ol><h1 id="Android面试题"><a href="#Android面试题" class="headerlink" title="Android面试题"></a>Android面试题</h1><h2 id="Android基础"><a href="#Android基础" class="headerlink" title="Android基础"></a>Android基础</h2><ol><li><p>什么是ANR？如何避免它？<br>看《ANR机制以及问题分析》</p></li><li><p>View的绘制流程？自定义View如何考虑机型适配？ View的事件分发机制？</p></li><li><p>ART和Dalvik的对比，JVM的内存 分配和垃圾回收GC机制,虚拟机原理，如何自己设计一个虚拟机(内存管理，类加载，双亲委派)，JVM的内存模型以及类加载机制，内存对象的循环应用以及避免</p></li></ol><ol start="4"><li>DDMS/traceView/systrace</li></ol><ol start="5"><li>内存回收机制与GC算法(各种算法的优缺点以及应用场景)；GC的原理时机以及GC对象；内存泄漏的场景以及解决方法</li></ol><ol start="6"><li>四大组件以及生命周期；ContentProvider的权限管理(读写分离，权限控制-精确到表级，URL控制)，Activity的四种启动模式的对比，Activity状态保存于恢复</li></ol><ol start="7"><li>什么是AIDL，以及如何使用.</li></ol><ol start="8"><li>请解释下在单线程模型中Message、Hanlder、MessageQueue、Looper之间的关系</li></ol><ol start="9"><li>Fragment生命周期，Fragment状态保存,startActivityForResult是哪个类的方法，在什么情况下使用，如果在Adapter中使用，应该如何解耦</li></ol><ol start="10"><li>AsyncTask 原理以及不足，IntentService的原理</li></ol><ol start="11"><li><p>Activity怎么和Service绑定，怎么在Activity中启动对应的Service</p></li><li><p>AsyncTask + HttpClient 与 AsyncHttpClient有什么区别</p></li><li><p>请描述一下Service的生命周期</p></li></ol><ol start="14"><li><p>如何保证一个后台服务不被杀死；比较省电的方式是什么？</p></li><li><p>如何通过广播拦截和abort一条短信；广播是否可以请求网络，广播引起的ANR的时间限制</p></li><li><p>进程间通信的方式有哪些？ AIDL</p></li><li><p>事件分发中的onTouch和onTouchEvent有什么区别，又该如何使用</p></li></ol><ol start="18"><li><p>说说 ContentProvider/ContentResolver/ContentObservr之间的关系</p></li><li><p>请介绍一下ContentProvider是如何实现数据共享的</p></li><li><p>Handler的机制以及底层的实现</p></li><li><p>Binder机制以及底层的实现</p></li><li><p>ListView中图片错位的问题是如何产生的</p></li><li><p>在manifest和代码中是如何注册和使用广播的</p></li><li><p>说说Activity/Intent/Service是什么关系</p></li><li><p>ApplicationContext和ActivityContext的区别是什么</p></li></ol><ol start="26"><li><p>一张Bitmap所占内存以及内存占用的计算方法</p></li><li><p>Serializable和Parcelable的区别</p></li><li><p>请描述一下BroadcastReceiver</p></li></ol><ol start="29"><li>请描述一下Android的事件分发机制</li></ol><ol start="30"><li><p>请介绍一下NDK</p></li><li><p>什么是NDK库，如何在JNI中注册native函数，有几种注册方式</p></li><li><p>Async如何使用</p></li></ol><ol start="33"><li><p>对于应用更新这块是如何做的？(灰度？强制更新？分区域更新？)</p></li><li><p>混合开发：ReactNative/weex/H5/小程序/Flutter</p></li><li><p>什么情况下会导致内存泄漏</p></li><li><p>如何对Android应用进行性能分析以及优化</p></li><li><p>说一款你认为当前比较火的应用并设计(直播APP)</p></li><li><p>OOM的避免异常以及解决方法</p></li><li><p>屏幕适配的处理技巧有哪些</p></li><li><p>两个Activity之间跳转时必然会执行的是哪几个方法</p></li></ol><ol start="41"><li><p>MVC、MVP、MVVM、常见的设计模式，写出观察者模式代码</p></li><li><p>TCP的三次握手和四次挥手，TCP与UDP的区别</p></li><li><p>HTTP协议；HTTP 1.0与2.0的区别，HTTP报文结构</p></li><li><p>都使用过哪些框架和平台</p></li><li><p>都使用过哪些自定义控件</p></li><li><p>介绍你做过的项目</p></li></ol><h2 id="非技术问题汇总"><a href="#非技术问题汇总" class="headerlink" title="非技术问题汇总"></a>非技术问题汇总</h2><ol><li><p>研究的比较深的领域有哪些</p></li><li><p>对业内信息的关注渠道有哪些</p></li><li><p>最近都读了哪些书</p></li><li><p>自己最擅长的技术点，最感兴趣的技术领域和技术点</p></li><li><p>项目中使用了哪些开源库，如何避免因为引入开源库而导致的安全性和稳定性问题</p></li><li><p>实习过程中做了什么，有哪些产出</p></li><li><p>5枚硬币，2正3反，如何划分为两堆然后通过翻转让两堆中正面向上的硬币和反面向上的硬币个数相同</p></li><li><p>时针走一圈，时针和分针重合几次</p></li><li><p>N*N的方格纸，里面有多少个正方形</p></li><li><p>现在下载速度很慢，试从网络协议的角度分析原因，并优化，网络的5层都可以涉及</p></li></ol><h1 id="数据结构和算法"><a href="#数据结构和算法" class="headerlink" title="数据结构和算法"></a>数据结构和算法</h1><ol><li><p>堆和栈在内存中的区别是什么(数据结构方面以及实际实现方面)</p></li><li><p>最快的排序算法是哪个？给阿里2万多员工按年龄排序应该选择哪个算法？堆和树的区别？写出快排代码，链表逆序代码</p></li><li><p>求1000以内的水仙花数以及40亿以内的水仙花数</p></li><li><p>子串包含问题(KMP算法)写代码实现</p></li><li><p>万亿级别的两个URL文件A和B，如何求出A和B的差集C</p></li><li><p>蚁群算法和蒙特卡洛算法</p></li><li><p>写出你所知道的排序算法以及时空复杂度，稳定性</p></li><li><p>百度POI中如何试下查找最近商家功能(坐标镜像+R树)</p></li><li><p>死锁的四个必要条件</p></li><li><p>常见的编码方式，utf-8变种中文占几个字节，int型占几个字节</p></li></ol><h1 id="HR问题"><a href="#HR问题" class="headerlink" title="HR问题"></a>HR问题</h1><ol><li><p>上家离职的原因</p></li><li><p>讲一件你印象最深的事情</p></li><li><p>介绍一个你影响最深的项目</p></li><li><p>介绍你最热爱最擅长的专业领域</p></li><li><p>公司实习最大的收获是什么</p></li><li><p>与上级意见不一致时，你怎么办</p></li><li><p>自己的优点和缺点是什么，并举例说明</p></li><li><p>你的学习方法是什么样的？实习过程中如何学习？实习过程中遇到的最大困难是什么，以及如何解决的？</p></li><li><p>说一件最能证明你能力的事情</p></li><li><p>针对你申请的这个职位，你认为你还欠缺什么</p></li><li><p>项目中遇到的最大困难时什么，如何解决</p></li><li><p>职业规划以及个人目标，未来发展路线以及求职定位</p></li></ol><ol start="13"><li></li></ol><h1 id="Java-基础-必须要完全的掌握，越熟练越好"><a href="#Java-基础-必须要完全的掌握，越熟练越好" class="headerlink" title="Java 基础 (必须要完全的掌握，越熟练越好)"></a>Java 基础 (必须要完全的掌握，越熟练越好)</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>  本部分内容主要包含以下知识点，这些内容都是Java中的基础知识，对于Java的学习很有帮助.其中集合、反射、IO等都是面试常问的知识点，是<strong>必须掌握</strong>的。</p><ul><li>Java集合</li><li>Java反射</li><li>Java注解</li><li>Java I/O</li><li>Java泛型</li><li>Cloneable </li><li>Serializable</li><li>Iterator</li></ul><h2 id="Java集合框架"><a href="#Java集合框架" class="headerlink" title="Java集合框架"></a>Java集合框架</h2><p>  Java集合大致可以分为Set、List、Queue和Map四种体系.</p><p>  其中Set 集合：代表无序，不可重复的集合;<br>  List：代表有序，重复的集合;<br>  Queue:是先进先出的队列集合;<br>  Map：代表映射关系的集合;</p><h3 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h3><p>  Java集合就像一种容器，可以把多个对象(实际上是对象的引用，但习惯上都称对象)”丢进”该容器中,从Java 5 增加了泛型之后，Java集合可以记住容器中对象的数据类型，使得编码更加简洁、健壮.</p><ol><li><p>Java集合和数组的区别：<br>a)数组长度在初始化的时候指定，意味着只能保存定长的数据。而集合可以保存数量不确定的数据，同时可以保存具有映射关系的数据(即关联数组，键值对key-value).<br>b)数组的元素既可以是基本数据类型，也可以是对象.集合中只能保存对象(实际上只是保存对象的引用变量)，基本数据类型的变量要转换成对应的包装类才能放入集合类中.</p></li><li><p>Java集合类之间的继承关系：<br>Java的集合类主要由两个接口派生而出：Collection和Map,Collection 和 Map是Java集合框架的根接口.<br>Collection的派生树 如下：<br><img src="https://camo.githubusercontent.com/b1974bdd58e78c12612907d0ad27b893f8fbdbb9/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d653766656266333634643864383233352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Collection的派生树"><br>图中后面[I]表示是接口，[C]表示是类，[AC]表示是抽象类</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Collection[I]</span><br><span class="line">└── Set[I] </span><br><span class="line">|     └── SortedSet[I]</span><br><span class="line">|     |         └── TreeSet[C]</span><br><span class="line">|     └── HashSet[C]</span><br><span class="line">|     |          └── LinkedHashSet[C]</span><br><span class="line">|     └── EnumSet[C]</span><br><span class="line">└── Queue[I]</span><br><span class="line">|      └── PriorityQueue[C]</span><br><span class="line">|      └── Deque[I]</span><br><span class="line">|              └── ArraryDeque[C]</span><br><span class="line">|              └── LinkedList[C]</span><br><span class="line">└── List[I]</span><br><span class="line">       └── LinkedList[C]</span><br><span class="line">       └── ArraryList[C]</span><br><span class="line">       |           └── AttributList[C]</span><br><span class="line">       └── Vector[C]</span><br><span class="line">               └── Stack[C]</span><br></pre></td></tr></table></figure><p>此图中，ArrayList，HashSet，LinkedList，TreeSet是我们经常会有用到的已实现的集合类.</p></li></ol><p>   Map 类实现用于保存具有映射关系的数据。Map保存的每项数据都是key-value对，<strong>Map里面的key是不可重复的，key用于表示集合里面的每一项数据.</strong>，这是Map和Collection最大的区别.<br>   <img src="https://camo.githubusercontent.com/517258d1f1e996c7a728c8fe067c439722fc61ce/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d303630353231303738343961373630332e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Map的派生树"><br>   图中后面[I]表示是接口，[C]表示是类，[AC]表示是抽象类<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Map</span><br><span class="line">└── SortMap</span><br><span class="line">|       └──  TreeMap</span><br><span class="line">└── WeakHashMap</span><br><span class="line">└── EnumMap</span><br><span class="line">└── HashMap </span><br><span class="line">|       └── LinkedHashMap</span><br><span class="line">└── IdentityHashMapMap</span><br><span class="line">└── HashTable</span><br><span class="line">        └── Properties</span><br></pre></td></tr></table></figure></p><p>   图中，HashMap、TreeMap是我们经常用到的集合类</p><h3 id="Collection-接口"><a href="#Collection-接口" class="headerlink" title="Collection 接口"></a>Collection 接口</h3><p>  Collection是 Set、List、Queue的父接口<br>  在接口中定义了多种方法提供给子类进行实现，以实现数据操作.<br>  Collection.java代码如下:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">// Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line">// Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public abstract interface Collection&lt;E&gt; extends java.lang.Iterable &#123;</span><br><span class="line">  </span><br><span class="line">  public abstract int size();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean isEmpty();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean contains(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Iterator&lt;E&gt; iterator();</span><br><span class="line">  </span><br><span class="line">  public abstract java.lang.Object[] toArray();</span><br><span class="line">  </span><br><span class="line">  public abstract &lt;T&gt; T[] toArray(T[] arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean add(E arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract boolean remove(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean containsAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean addAll(java.util.Collection&lt;? extends E&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean removeAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public  boolean removeIf(java.util.function.Predicate&lt;? super E&gt; arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean retainAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract void clear();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean equals(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract int hashCode();</span><br><span class="line">  </span><br><span class="line">  public  java.util.Spliterator&lt;E&gt; <span class="function"><span class="title">spliterator</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  java.util.stream.Stream&lt;E&gt; <span class="function"><span class="title">stream</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  java.util.stream.Stream&lt;E&gt; <span class="function"><span class="title">parallelStream</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>从<a href="http://tool.oschina.net/apidocs/apidoc?api=jdk-zh" target="_blank" rel="noopener">JDK文档</a>可以查看到每个函数的具体作用.</p><p>可以看出Collection用法有：添加元素、删除元素，返回Collection集合个数以及清空集合等.其中重点介绍iterator()方法，该方法的返回值是Iterator.</p><p>从Collection的源码看到，Iterable是Collection的父接口,Iterable的源码如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line">// Implementation of methods is unavailable.</span><br><span class="line">package java.lang;</span><br><span class="line">public abstract interface Iterable&lt;T&gt; &#123;</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Iterator&lt;T&gt; iterator();</span><br><span class="line">  </span><br><span class="line">  public  void forEach(java.util.function.Consumer&lt;? super T&gt; arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  java.util.Spliterator&lt;T&gt; <span class="function"><span class="title">spliterator</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">``` </span><br><span class="line">可以看到Iterable接口中有一个抽象方法iterator()</span><br><span class="line">``` bash</span><br><span class="line">public abstract  java.util.Iterator&lt;T&gt; iterator();</span><br></pre></td></tr></table></figure></p><p>iterator()方法的返回类型是Iterator型，我们再看看Iterator的源码:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> // Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line"> // Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public abstract interface Iterator&lt;E&gt; &#123;</span><br><span class="line">  </span><br><span class="line">  public abstract boolean hasNext();</span><br><span class="line">  </span><br><span class="line">  public abstract  E next();</span><br><span class="line">  </span><br><span class="line">  public void <span class="function"><span class="title">remove</span></span>() &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  void forEachRemaining(java.util.function.Consumer&lt;? super E&gt; arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>从JDK文档中可以看到Iterator 是对 collection 进行迭代的迭代器.主要作用是遍历Collection里面的元素.</p><h3 id="Set-集合"><a href="#Set-集合" class="headerlink" title="Set 集合"></a>Set 集合</h3><p>  Set集合和Collection集合完全相同，没有提供额外的方法，只是行为略有不同(Set不允许包含重复元素).<br>  我们来看看Set的源码.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"> // Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line"> // Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public abstract interface Set&lt;E&gt; extends java.util.Collection &#123;</span><br><span class="line">  </span><br><span class="line">  public abstract int size();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean isEmpty();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean contains(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Iterator&lt;E&gt; iterator();</span><br><span class="line">  </span><br><span class="line">  public abstract java.lang.Object[] toArray();</span><br><span class="line">  </span><br><span class="line">  public abstract &lt;T&gt; T[] toArray(T[] arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean add(E arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract boolean remove(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean containsAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean addAll(java.util.Collection&lt;? extends E&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean retainAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  boolean removeAll(java.util.Collection&lt;?&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract void clear();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean equals(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract int hashCode();</span><br><span class="line">  </span><br><span class="line">  public  java.util.Spliterator&lt;E&gt; <span class="function"><span class="title">spliterator</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>Set.java也是一个接口，从Set.java和Collection.java的代码来看，几乎一样的.</p><p>通过查看源码，我们发现最后Set集合的类，都是继承自AbstractSet类，实现了Set、Cloneable、Serializable接口<br>以HashSet为例，看下它的源码：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"> // Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line"> // Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public class HashSet&lt;E&gt; extends java.util.AbstractSet implements java.util.Set, java.lang.Cloneable, java.io.Serializable &#123;</span><br><span class="line">  </span><br><span class="line">  static final long serialVersionUID = -5024744406713321676L;</span><br><span class="line">  </span><br><span class="line">  private transient java.util.HashMap&lt;E,java.lang.Object&gt; map;</span><br><span class="line">  </span><br><span class="line">  private static final java.lang.Object PRESENT;</span><br><span class="line">  </span><br><span class="line">  public <span class="function"><span class="title">HashSet</span></span>() &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  HashSet(java.util.Collection&lt;? extends E&gt; arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public HashSet(int arg0, <span class="built_in">float</span> arg1) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public HashSet(int arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  HashSet(int arg0, <span class="built_in">float</span> arg1, boolean arg2) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  java.util.Iterator&lt;E&gt; <span class="function"><span class="title">iterator</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public int <span class="function"><span class="title">size</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> 0;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public boolean <span class="function"><span class="title">isEmpty</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public boolean contains(java.lang.Object arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  boolean add(E arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public boolean remove(java.lang.Object arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public void <span class="function"><span class="title">clear</span></span>() &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public java.lang.Object <span class="function"><span class="title">clone</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  private void writeObject(java.io.ObjectOutputStream arg0) throws java.io.IOException &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  private void readObject(java.io.ObjectInputStream arg0) throws java.io.IOException, java.lang.ClassNotFoundException &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  java.util.Spliterator&lt;E&gt; <span class="function"><span class="title">spliterator</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  static &#123;&#125; &#123;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们来看看AbstractSet这个抽象类的源码：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"> // Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line"> // Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public abstract class AbstractSet&lt;E&gt; extends java.util.AbstractCollection implements java.util.Set &#123;</span><br><span class="line">  </span><br><span class="line">  protected <span class="function"><span class="title">AbstractSet</span></span>() &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public boolean equals(java.lang.Object arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public int <span class="function"><span class="title">hashCode</span></span>() &#123;</span><br><span class="line">    <span class="built_in">return</span> 0;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  boolean removeAll(java.util.Collection&lt;?&gt; arg0) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>查阅JDK api看到对AbstractSet类的解释是：此类提供 Set 接口的骨干实现，从而最大限度地减少了实现此接口所需的工作。<br>看到这里有点懵了，</p><p>  Map.java代码如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">// Failed to get sources. Instead, stub sources have been generated by the disassembler.</span><br><span class="line">// Implementation of methods is unavailable.</span><br><span class="line">package java.util;</span><br><span class="line">public abstract interface Map&lt;K,V&gt; &#123;</span><br><span class="line">  </span><br><span class="line">  public abstract int size();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean isEmpty();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean containsKey(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract boolean containsValue(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  V get(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  V put(K arg0, V arg1);</span><br><span class="line">  </span><br><span class="line">  public abstract  V remove(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract  void putAll(java.util.Map&lt;? extends K,? extends V&gt; arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract void clear();</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Set&lt;K&gt; keySet();</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Collection&lt;V&gt; values();</span><br><span class="line">  </span><br><span class="line">  public abstract  java.util.Set&lt;java.util.Map.Entry&lt;K,V&gt;&gt; entrySet();</span><br><span class="line">  </span><br><span class="line">  public abstract boolean equals(java.lang.Object arg0);</span><br><span class="line">  </span><br><span class="line">  public abstract int hashCode();</span><br><span class="line">  </span><br><span class="line">  public  V getOrDefault(java.lang.Object arg0, V arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  void forEach(java.util.function.BiConsumer&lt;? super K,? super V&gt; arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  void replaceAll(java.util.function.BiFunction&lt;? super K,? super V,? extends V&gt; arg0) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V putIfAbsent(K arg0, V arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public boolean remove(java.lang.Object arg0, java.lang.Object arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  boolean replace(K arg0, V arg1, V arg2) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V replace(K arg0, V arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V computeIfAbsent(K arg0, java.util.function.Function&lt;? super K,? extends V&gt; arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V computeIfPresent(K arg0, java.util.function.BiFunction&lt;? super K,? super V,? extends V&gt; arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V compute(K arg0, java.util.function.BiFunction&lt;? super K,? super V,? extends V&gt; arg1) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  public  V merge(K arg0, V arg1, java.util.function.BiFunction&lt;? super V,? super V,? extends V&gt; arg2) &#123;</span><br><span class="line">    <span class="built_in">return</span> null;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="Java泛型"><a href="#Java泛型" class="headerlink" title="Java泛型"></a>Java泛型</h2><h3 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h3><ol><li><p>引入泛型的目的<br>了解引入泛型的动机，就先从语法糖开始了解</p><p><strong>语法糖</strong></p><p>语法糖(Synactic Sugar),也称糖衣语法，英国计算机学家Peter.J.Landin发明的一个术语，指在计算机语言中添加某种语法，这种语法对语言的功能并没有影响，但是更方便程序员使用.</p><p>Java中最常用的语法糖主要有泛型、变长参数、条件编译、自动拆装箱、内部类等.虚拟机并不支持这些语法，它们在编译阶段被还原回了简单的基础语法结构，这个过程成为解语法糖.</p><p>泛型的目的：泛型的目的是使得在编译阶段完成一些类型转换的工作，避免在运行时强制类型转换而出现ClassCastException，即类型转换异常.</p></li><li><p>泛型初探<br>JDK 1.5 才增加了泛型，并在很大程度上都是方便集合的使用，使其能够记住其元素的数据类型.<br>在泛型（Generic type或Generics）出现之前，是这么写代码的：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)</span><br><span class="line">&#123;</span><br><span class="line">    List list = new ArrayList();</span><br><span class="line">    list.add(<span class="string">"123"</span>);</span><br><span class="line">    list.add(<span class="string">"456"</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println((String)list.get(0));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然这是完全允许的，因为List里面的内容是Object类型的，自然任何对象类型都可以放入、都可以取出，但是这么写会有两个问题：</p><p>1、当一个对象放入集合时，集合不会记住此对象的类型，当再次从集合中取出此对象时，该对象的编译类型变成了Object。<br>2、运行时需要人为地强制转换类型到具体目标，实际的程序绝不会这么简单，一个不小心就会出现java.lang.ClassCastException。</p><p>所以，泛型出现之后，上面的代码就改成了大家都熟知的写法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)</span><br><span class="line">&#123;</span><br><span class="line">    List&lt;String&gt; list = new ArrayList&lt;String&gt;();</span><br><span class="line">    list.add(<span class="string">"123"</span>);</span><br><span class="line">    list.add(<span class="string">"456"</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(list.get(0));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这就是泛型。泛型是对Java语言类型系统的一种扩展，有点类似于C++的模板，可以把类型参数看作是使用参数化类型时指定的类型的一个占位符。引入泛型，是对Java语言一个较大的功能增强，带来了很多的好处。</p></li><li><p>泛型的好处<br>①类型安全。类型错误现在在编译期间就被捕获到了，而不是在运行时当作java.lang.ClassCastException展示出来，将类型检查从运行时挪到编译时有助于开发者更容易找到错误，并提高程序的可靠性。</p><p>②消除了代码中许多的强制类型转换，增强了代码的可读性。</p><p>③为较大的优化带来了可能。</p></li></ol><h3 id="泛型的使用"><a href="#泛型的使用" class="headerlink" title="泛型的使用"></a>泛型的使用</h3><ol><li><p>泛型类和泛型接口<br>下面是JDK 1.5 以后，List接口，以及ArrayList类的代码片段.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">//定义接口时指定了一个类型形参，该形参名为E</span><br><span class="line">public interface List&lt;E&gt; extends Collection&lt;E&gt; &#123;</span><br><span class="line">   //在该接口里，E可以作为类型使用</span><br><span class="line">   public E get(int index) &#123;&#125;</span><br><span class="line">   public void add(E e) &#123;&#125; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">//定义类时指定了一个类型形参，该形参名为E</span><br><span class="line">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; implements List&lt;E&gt; &#123;</span><br><span class="line">   //在该类里，E可以作为类型使用</span><br><span class="line">   public void <span class="built_in">set</span>(E e) &#123;</span><br><span class="line">   .......................</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这就是<strong>泛型的实质：允许在定义接口、类时声明类型形参，类型形参在整个接口、类体内可当成类型使用，几乎所有可使用普通类型的地方都可以使用这种类型形参。</strong></p><p>下面具体讲解泛型类的使用。泛型接口的使用与泛型类几乎相同，可以比对自行学习。</p><p><strong>泛型类</strong></p><p>定义一个容器类，存放键值对key-value，键值对的类型不确定，可以使用泛型来定义，分别指定为K和V。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">public class Container&lt;K, V&gt; &#123;</span><br><span class="line"></span><br><span class="line">    private K key;</span><br><span class="line">    private V value;</span><br><span class="line"></span><br><span class="line">    public Container(K k, V v) &#123;</span><br><span class="line">        key = k;</span><br><span class="line">        value = v;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public K <span class="function"><span class="title">getkey</span></span>() &#123;</span><br><span class="line">        <span class="built_in">return</span> key;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public V <span class="function"><span class="title">getValue</span></span>() &#123;</span><br><span class="line">        <span class="built_in">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void <span class="function"><span class="title">setKey</span></span>() &#123;</span><br><span class="line">        this.key = key;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void <span class="function"><span class="title">setValue</span></span>() &#123;</span><br><span class="line">        this.value = value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在使用Container类时，只需要指定K，V的具体类型即可，从而创建出逻辑上不同的Container实例，用来存放不同的数据类型。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) &#123;</span><br><span class="line">    Container&lt;String,String&gt;  c1=new Container&lt;String ,String&gt;(<span class="string">"name"</span>,<span class="string">"hello"</span>);</span><br><span class="line">    Container&lt;String,Integer&gt; c2=new Container&lt;String,Integer&gt;(<span class="string">"age"</span>,22);</span><br><span class="line">    Container&lt;Double,Double&gt;  c3=new Container&lt;Double,Double&gt;(1.1,1.3);</span><br><span class="line">    System.out.println(c1.getKey() + <span class="string">" : "</span> + c1.getValue());      </span><br><span class="line">    System.out.println(c2.getKey() + <span class="string">" : "</span> + c2.getValue());                                                               </span><br><span class="line">    System.out.println(c3.getKey() + <span class="string">" : "</span> + c3.getValue());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在JDK 1.7 增加了泛型的“菱形”语法：<strong>Java允许在构造器后不需要带完成的泛型信息，只要给出一对尖括号（&lt;&gt;）即可，Java可以推断尖括号里应该是什么泛型信息。</strong><br>如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Container&lt;String,String&gt; c1=new Container&lt;&gt;(<span class="string">"name"</span>,<span class="string">"hello"</span>);</span><br><span class="line">Container&lt;String,Integer&gt; c2=new Container&lt;&gt;(<span class="string">"age"</span>,22);</span><br></pre></td></tr></table></figure><p><strong>泛型类派生子类</strong></p><p>当创建了带泛型声明的接口、父类之后，可以为该接口创建实现类，或者从该父类派生子类，需要注意：<strong>使用这些接口、父类派生子类时不能再包含类型形参，需要传入具体的类型。</strong><br>错误的方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public class A extends Container&lt;K, V&gt;&#123;&#125;</span><br></pre></td></tr></table></figure><p>正确的方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public class A extends Container&lt;Integer, String&gt;&#123;&#125;</span><br></pre></td></tr></table></figure><p>也可以不指定具体的类型，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public class A extends Container&#123;&#125;</span><br></pre></td></tr></table></figure><p>此时系统会把K,V形参当成Object类型处理。</p></li><li><p>泛型的方法<br>前面在介绍泛型类和泛型接口中提到，可以在泛型类、泛型接口的方法中，把泛型中声明的类型形参当成普通类型使用。 如下面的方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">public class Container&lt;K, V&gt;</span><br><span class="line"> &#123;</span><br><span class="line">........................</span><br><span class="line">    public K <span class="function"><span class="title">getkey</span></span>() &#123;</span><br><span class="line">        <span class="built_in">return</span> key;</span><br><span class="line">    &#125;</span><br><span class="line">    public void <span class="function"><span class="title">setKey</span></span>() &#123;</span><br><span class="line">        this.key = key;</span><br><span class="line">    &#125;</span><br><span class="line">....................</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但在另外一些情况下，在类、接口中没有使用泛型时，定义方法时想定义类型形参，就会使用泛型方法。如下方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">public class Main&#123;</span><br><span class="line">  public static &lt;T&gt; void out(T t)&#123;</span><br><span class="line">       System.out.println(t);</span><br><span class="line">  &#125;</span><br><span class="line">  public static void main(String[] args)&#123;</span><br><span class="line">       out(<span class="string">"hansheng"</span>);</span><br><span class="line">       out(123);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>所谓泛型方法，就是在声明方法时定义一个或多个类型形参。 泛型方法的用法格式如下：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">修饰符&lt;T, S&gt; 返回值类型 方法名（形参列表）｛</span><br><span class="line">   方法体</span><br><span class="line">｝</span><br></pre></td></tr></table></figure><p><strong>注意：</strong> 方法声明中定义的形参只能在该方法里使用，而接口、类声明中定义的类型形参则可以在整个接口、类中使用。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">class Demo&#123;  </span><br><span class="line">  public &lt;T&gt; T fun(T t)&#123;   // 可以接收任意类型的数据  </span><br><span class="line">   <span class="built_in">return</span> t ;     // 直接把参数返回  </span><br><span class="line">  &#125;  </span><br><span class="line">&#125;;  </span><br><span class="line">public class GenericsDemo26&#123;  </span><br><span class="line">  public static void main(String args[])&#123;  </span><br><span class="line">    Demo d = new Demo() ; // 实例化Demo对象  </span><br><span class="line">    String str = d.fun(<span class="string">"汤姆"</span>) ; // 传递字符串  </span><br><span class="line">    int i = d.fun(30) ;  // 传递数字，自动装箱  </span><br><span class="line">    System.out.println(str) ; // 输出内容  </span><br><span class="line">    System.out.println(i) ;  // 输出内容  </span><br><span class="line">  &#125;  </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>当调用fun()方法时，根据传入的实际对象，编译器就会判断出类型形参T所代表的实际类型。</p></li><li><p>泛型构造器<br>正如泛型方法允许在方法签名中声明类型形参一样，Java也允许在构造器签名中声明类型形参，这样就产生了所谓的泛型构造器。<br>和使用普通泛型方法一样没区别，一种是显式指定泛型参数，另一种是隐式推断，如果是显式指定则以显式指定的类型参数为准，如果传入的参数的类型和指定的类型实参不符，将会编译报错。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class Person &#123;</span><br><span class="line">    public &lt;T&gt; Person(T t) &#123;</span><br><span class="line">        System.out.println(t);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">        //隐式</span><br><span class="line">        new Person(22);</span><br><span class="line">        //显示</span><br><span class="line">        new&lt;String&gt; Person(<span class="string">"hello"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里唯一需要特殊注明的就是，如果构造器是泛型构造器，同时该类也是一个泛型类的情况下应该如何使用泛型构造器：<br>因为泛型构造器可以显式指定自己的类型参数（需要用到菱形，放在构造器之前），而泛型类自己的类型实参也需要指定（菱形放在构造器之后），这就同时出现了两个菱形了，这就会有一些小问题，具体用法再这里总结一下。<br>以下面这个例子为代表</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public class Person&lt;E&gt; &#123;</span><br><span class="line">    public &lt;T&gt; Person(T t) &#123;</span><br><span class="line">        System.out.println(t);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种用法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Person&lt;String&gt; a = new &lt;Integer&gt;Person&lt;&gt;(15);</span><br></pre></td></tr></table></figure><p>这种语法不允许，会直接编译报错！</p></li></ol><h3 id="类型通配符"><a href="#类型通配符" class="headerlink" title="类型通配符"></a>类型通配符</h3><p> 顾名思义就是匹配任意类型的类型实参.<br> 类型通配符是一个问号（？)，将一个问号作为类型实参传给List集合，写作：List&lt;?&gt;（意思是元素类型未知的List）。这个问号（？）被成为通配符，它的元素类型可以匹配任何类型。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">public void <span class="built_in">test</span>(List&lt;?&gt; c)&#123;</span><br><span class="line">  <span class="keyword">for</span>(int i =0;i&lt;c.size();i++)&#123;</span><br><span class="line">    System.out.println(c.get(i));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 现在可以传入任何类型的List来调用test()方法，程序依然可以访问集合c中的元素，其类型是Object。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">List&lt;?&gt; c = new ArrayList&lt;String&gt;();</span><br><span class="line">//编译器报错</span><br><span class="line">c.add(new Object());</span><br></pre></td></tr></table></figure></p><p> 但是并不能把元素加入到其中。因为程序无法确定c集合中元素的类型，所以不能向其添加对象。<br> 下面就该引入带限通配符，来确定集合元素中的类型。</p><p> <strong>带限通配符</strong><br> 单来讲，使用通配符的目的是来限制泛型的类型参数的类型，使其满足某种条件，固定为某些类。<br> 主要分为两类即：<strong>上限通配符</strong>和<strong>下限通配符</strong>。</p><ol><li><p>上限通配符<br>如果想限制使用泛型类别时，只能用某个特定类型或者是其<strong>子类型</strong>才能实例化该类型时，可以在定义类型时，<strong>使用extends关键字指定这个类型必须是继承某个类，或者实现某个接口，也可以是这个类或接口本身</strong></p><p>表示集合中的所有元素都是Shape类型或者其子类</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;? extends Shape&gt;</span><br></pre></td></tr></table></figure><p>这就是所谓的上限通配符，使用关键字extends来实现，实例化时，指定类型实参只能是extends后类型的子类或其本身。<br>例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">//Circle是其子类</span><br><span class="line">List&lt;? extends Shape&gt; list = new ArrayList&lt;Circle&gt;();</span><br></pre></td></tr></table></figure><p>这样就确定集合中元素的类型，虽然不确定具体的类型，但最起码知道其父类。然后进行其他操作。</p></li><li><p>下限通配符<br>如果想限制使用泛型类别时，只能用某个特定类型或者是其<strong>父类型</strong>才能实例化该类型时，可以在定义类型时，<strong>使用super关键字指定这个类型必须是是某个类的父类，或者是某个接口的父接口，也可以是这个类或接口本身。</strong></p><p>表示集合中的所有元素都是Circle类型或者其父类</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List &lt;? super Circle&gt;</span><br></pre></td></tr></table></figure><p>这就是所谓的下限通配符，使用关键字super来实现，实例化时，指定类型实参只能是extends后类型的子类或其本身。<br>例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">//Shape是其父类</span><br><span class="line">List&lt;? super Circle&gt; list = new ArrayList&lt;Shape&gt;();</span><br></pre></td></tr></table></figure></li></ol><h3 id="类型擦除"><a href="#类型擦除" class="headerlink" title="类型擦除"></a>类型擦除</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Class c1=new ArrayList&lt;Integer&gt;().getClass();</span><br><span class="line">Class c2=new ArrayList&lt;String&gt;().getClass();</span><br><span class="line">System.out.println(c1==c2);</span><br></pre></td></tr></table></figure><p> 程序输出：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">true</span></span><br></pre></td></tr></table></figure></p><p>这是因为不管为泛型的类型形参传入哪一种类型实参，对于Java来说，它们依然被当成同一类处理，在内存中也只占用一块内存空间。从Java泛型这一概念提出的目的来看，其只是作用于代码编译阶段，在编译过程中，对于正确检验泛型结果后，会将泛型的相关信息擦出，也就是说，成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。</p><p><strong>在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。由于系统中并不会真正生成泛型类，所以instanceof运算符后不能使用泛型类。</strong></p><h2 id="Java-注解"><a href="#Java-注解" class="headerlink" title="Java 注解"></a>Java 注解</h2><h3 id="元数据"><a href="#元数据" class="headerlink" title="元数据"></a>元数据</h3><p> 要理解注解(Annotation)作用，就要先理解Java中的元数据的概念。</p><ol><li><p>元数据概念<br>元数据是关于数据的数据，在编程语言上下文中，元数据是添加到程序元素如方法、字段、类和包上的额外信息。对数据进行说明描述的数据。</p></li><li><p>元数据的作用<br>一般来说，元数据可以用于创建文档(根据程序元素上的注释创建文档),跟踪代码中的依赖性(可声明方法是重载，依赖父类的方法)，执行编译时检查(可声明是否编译期监测)，代码分析。<br>如下：<br>1) 编写文档： 通过代码里标识的元数据生成文档<br>2) 代码分析： 通过代码里标识的元数据对代码进行分析<br>3）编译检查： 通过代码里标识的元数据让编译器能实现基本的编译检查</p></li><li><p>Java平台元数据<br>注解Annotation就是Java平台的元数据，是J2SE5.0新增加的功能，该机制允许在Java代码中添加自定义注释，并允许通过反射(reflection),以编程方式访问元数据注释。通过提供为程序元素(类、方法等)附加额外数据的标准方法，元数据功能具有简化和改进许多应用程序开发领域的潜在能力，其中包括配置管理、框架实现和代码生成。</p></li></ol><h3 id="注解-Annotation"><a href="#注解-Annotation" class="headerlink" title="注解(Annotation)"></a>注解(Annotation)</h3><ol><li><p>注解(Annotation)的概念<br>注解是在JDK1.5之后新增加的一个新特性，注解的引入意义很大，有很多非常有名的框架，比如Hibernate、Spring等框架中都大量使用注解。注解作为程序的元数据嵌入到程序。注解可以被解析工具或者编译工具解析。</p><p>关于注解(Annotation)的作用，其实就是上述元数据的作用。</p><p><strong>注意：Annotation能被用来为程序元素(类、方法、成员变量等)设置元数据。Annotation不影响程序代码的执行，无论增加、删除Annotation，代码始终如一的执行。如果希望让程序中的Annotation起一定的作用，只有通过解析工具或者编译工具对Annotation中的信息进行解析和处理.</strong></p></li><li><p>内建注解<br>Java提供了多种内建的注解，下面接下几个比较常用的注解：@Override、@Deprecated、@SuppresWarnings以及@FunctionallInterface者四个注解。内建注解主要实现呢了元数据的第二个作用：<strong>编译检查</strong>。</p><p><strong>@Override</strong><br>用途：用于告知编译器，我们需要覆写超类的当前方法。如果某个方法带有该注解但并没有覆写超类相应的方法，则编译器会生成一条错误信息。如果父类没有这个要覆写的方法，则编译器也会生成一条错误信息。</p><p>@Override可适用元素为方法，仅仅保留在Java源文件中。</p><p><strong>@Deprecated</strong><br>用途：使用这个注解，用于告知编译器吗，某一程序元素(比如方法，成员变量)不建议使用了(即过时了).<br>例如：<br>Person类中的info()方法使用@Deprecated表示该方法过时了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public class Person &#123;</span><br><span class="line">    @Deprecated</span><br><span class="line">    public void <span class="function"><span class="title">info</span></span>()&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用info()方法会编译器会出现警告，告知该方法已过时。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public class Main&#123;</span><br><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">Person person = nre Person();</span><br><span class="line">person.info();//info函数会有一个删除线</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注解类型分析：@Deprecated可适合用于除注解类型声明之外的所有元素，保留时长为运行时。</p></li></ol><p><strong>@SuppressWarnings</strong><br> 用途：用于告知编译器忽略特定的警告信息，例在泛型中使用原生数据类型，编译器会发出警告，当使用该注解后，则不会发出警告。<br> 注解类型分析： @SuppressWarnings可适合用于除注解类型声明和包名之外的所有元素，仅仅保留在java源文件中。</p><p> 该注解有方法value(）,可支持多个字符串参数，用户指定忽略哪种警告，例如：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@SupressWarning(value=&#123;<span class="string">"uncheck"</span>,<span class="string">"deprecation"</span>&#125;)</span><br></pre></td></tr></table></figure></p><p> <img src="https://camo.githubusercontent.com/bd0738100994d5221b0ae2783ae2db6851d0591f/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d323465333963646166306436326337352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="SuppressWarning参数"></p><p> <strong>@FunctionalInterface</strong><br> 用途：用户告知编译器，检查这个接口，保证该接口是函数式接口，即只能包含一个抽象方法，否则就会编译出错。</p><p> 注解类型分析： @FunctionalInterface可适合用于注解类型声明，保留时长为运行时。</p><ol start="3"><li><p>元Annotation<br>JDK除了在java.lang提供了上述内建注解外，还在java.lang.annotation包下提供了6个Meta Annotation(元Annotataion)，其中有5个元Annotation都用于修饰其他的Annotation定义。其中@Repeatable专门用户定义Java 8 新增的可重复注解。</p><p>我们先介绍其中4个常用的修饰其他Annotation的元Annotation。在此之前，我们先了解如何自定义Annotation。</p><p><strong>当一个接口直接继承java.lang.annotation.Annotation接口时，仍是接口，而并非注解。要想自定义注解类型，只能通过@interface关键字的方式，其实通过该方式会隐含地继承.Annotation接口。</strong></p><p><strong>@Documented</strong></p><p>@Documented用户指定被该元Annotation修饰的Annotation类将会被javadoc工具提取成文档，如果定义Annotation类时使用了@Documented修饰，则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。<br>例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@Documented</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Target(value=&#123;CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE&#125;)</span><br><span class="line">public @interface Deprecated &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>定义@Deprecated时使用了@Documented，则任何元素使用@Deprecated修饰时，在生成API文档时，将会包含@Deprecated的说明<br>以下是String的一个过时的构造方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@Deprecated</span><br><span class="line">public String(byte[] ascii,int hibyte,int offset, int count)</span><br></pre></td></tr></table></figure><p>该注解实现了元数据的第一个功能：<strong>编写文档</strong>。</p><p><strong>@Inherited</strong><br>@Retention：表示该注解类型的注解保留的时长。当注解类型声明中没有@Retention元注解，则默认保留策略为RetentionPolicy.CLASS。关于保留策略(RetentionPolicy)是枚举类型，共定义3种保留策略，如下表：<br><img src="https://camo.githubusercontent.com/6ed7adc069270fc38dc3975727e7c52eb0ef6f05/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d383238666536386663646638333462342e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Retention"></p><p><strong> @Target</strong><br>@Target：表示该注解类型的所适用的程序元素类型。当注解类型声明中没有@Target元注解，则默认为可适用所有的程序元素。如果存在指定的@Target元注解，则编译器强制实施相应的使用限制。关于程序元素(ElementType)是枚举类型，共定义8种程序元素，如下表：<br><img src="https://camo.githubusercontent.com/9dec3ec2d382a6c13ed4f7742e9abf9b8a653c11/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d376234353764663231343366613564642e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Target"></p></li></ol><h3 id="自定义注解（Annotation）"><a href="#自定义注解（Annotation）" class="headerlink" title="自定义注解（Annotation）"></a>自定义注解（Annotation）</h3><p> 创建自定义注解，与创建接口有几分相似，但注解需要以@开头。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">@Documented</span><br><span class="line">@Target(ElementType.METHOD)</span><br><span class="line">@Inherited</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">public @interface MyAnnotataion&#123;</span><br><span class="line">    String name();</span><br><span class="line">    String website() default <span class="string">"hello"</span>;</span><br><span class="line">    int revision() default 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> <strong>自定义注解中定义成员变量的规则：</strong><br> 其定义是以无形参的方法形式来声明的。即：<br> 注解方法不带参数，比如name()，website()；<br> 注解方法返回值类型：基本类型、String、Enums、Annotation以及前面这些类型的数组类型<br> 注解方法可有默认值，比如default “hello”，默认website=”hello”</p><p> <strong>当然注解中也可以不存在成员变量，在使用解析注解进行操作时，仅以是否包含该注解来进行操作。当注解中有成员变量时，若没有默认值，需要在使用注解时，指定成员变量的值。</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class AnnotationDemo &#123;</span><br><span class="line">    @MyAnnotataion(name=<span class="string">"lvr"</span>, website=<span class="string">"hello"</span>, revision=1)</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        System.out.println(<span class="string">"I am main method"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @SuppressWarnings(&#123; <span class="string">"unchecked"</span>, <span class="string">"deprecation"</span> &#125;)</span><br><span class="line">    @MyAnnotataion(name=<span class="string">"lvr"</span>, website=<span class="string">"hello"</span>, revision=2)</span><br><span class="line">    public void <span class="function"><span class="title">demo</span></span>()&#123;</span><br><span class="line">        System.out.println(<span class="string">"I am demo method"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  由于该注解的保留策略为RetentionPolicy.RUNTIME，故可在运行期通过反射机制来使用，否则无法通过反射机制来获取。这时候注解实现的就是元数据的第二个作用：<strong>代码分析</strong>。<br>下面来具体介绍如何通过反射机制来进行注解解析。</p><h3 id="注解解析"><a href="#注解解析" class="headerlink" title="注解解析"></a>注解解析</h3><p> 接下来，通过反射技术来解析自定义注解。关于反射类位于包java.lang.reflect，其中有一个接口AnnotatedElement，该接口主要有如下几个实现类：Class，Constructor，Field，Method，Package。除此之外，该接口定义了注释相关的几个核心方法，如下：<br> <img src="https://camo.githubusercontent.com/9fc1c7b7bd804977ad256f8b7b8ddf7fb1eddb2a/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d343037376262616566356232376134622e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Reflect"></p><p> 因此，当获取了某个类的Class对象，然后获取其Field,Method等对象，通过上述4个方法提取其中的注解，然后获得注解的详细信息。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public class AnnotationParser &#123;</span><br><span class="line">    public static void main(String[] args) throws SecurityException, ClassNotFoundException &#123;</span><br><span class="line">        String clazz = <span class="string">"com.lvr.annotation.AnnotationDemo"</span>;</span><br><span class="line">        Method[]  demoMethod = AnnotationParser.class</span><br><span class="line">                .getClassLoader().loadClass(clazz).getMethods();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (Method method : demoMethod) &#123;</span><br><span class="line">            <span class="keyword">if</span> (method.isAnnotationPresent(MyAnnotataion.class)) &#123;</span><br><span class="line">                 MyAnnotataion annotationInfo = method.getAnnotation(MyAnnotataion.class);</span><br><span class="line">                 System.out.println(<span class="string">"method: "</span>+ method);</span><br><span class="line">                 System.out.println(<span class="string">"name= "</span>+ annotationInfo.name() +</span><br><span class="line">                         <span class="string">" , website= "</span>+ annotationInfo.website()</span><br><span class="line">                        + <span class="string">" , revision= "</span>+annotationInfo.revision());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上仅是一个示例，其实可以根据拿到的注解信息做更多有意义的事。</p><h2 id="Java-反射"><a href="#Java-反射" class="headerlink" title="Java 反射"></a>Java 反射</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><ol><li><p>Java反射机制的定义<br>Java反射机制是在运行状态中，对于任意一个类，都能够知道这个类中的所有属性和方法；对于任意一个对象，都能够调用它的任意一个方法和属性；这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。</p></li><li><p>Java反射机制的功能<br>a).在运行时判断任意一个对象所属的类。<br>b).在运行时构造任意一个类的对象。<br>c).在运行时判断任意一个类所具有的成员变量和方法。<br>d).在运行时调用任意一个对象的方法。<br>e).生成动态代理。</p></li><li><p>Java 反射机制的应用场景<br>a).逆向代码  例如反编译<br>b).与注解相结合的框架 例如Retrofit<br>c).单纯的反射机制应用框架 例如EventBus<br>d).动态生成类框架 例如Gson</p></li></ol><h3 id="通过Java反射查看类信息"><a href="#通过Java反射查看类信息" class="headerlink" title="通过Java反射查看类信息"></a>通过Java反射查看类信息</h3><p> 一、 获得Class对象<br> 每个类被加载之后，系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。<br> 在Java程序中获得Class对象通常有如下三种方式：</p><ol><li>使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数，该字符串参数的值是某个类的全限定名（必须添加完整包名）。</li><li>调用某个类的class属性来获取该类对应的Class对象。</li><li><p>调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">//第一种方式 通过Class类的静态方法——forName()来实现</span><br><span class="line">class1 = Class.forName(<span class="string">"com.lvr.reflection.Person"</span>);</span><br><span class="line">//第二种方式 通过类的class属性</span><br><span class="line">class1 = Person.class;</span><br><span class="line">//第三种方式 通过对象getClass方法</span><br><span class="line">Person person = new Person();</span><br><span class="line">Class&lt;?&gt; class1 = person.getClass();</span><br></pre></td></tr></table></figure><p>二、获取class对象的属性、方法、构造函数等</p></li><li><p>获取class对象的成员变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性</span><br><span class="line">Field[] publicFields = class1.getFields();//获取class对象的public属性</span><br><span class="line">Field ageField = class1.getDeclaredField(<span class="string">"age"</span>);//获取class指定属性</span><br><span class="line">Field desField = class1.getField(<span class="string">"des"</span>);//获取class指定的public属性</span><br></pre></td></tr></table></figure></li><li><p>获取class对象的方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法</span><br><span class="line">Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法</span><br><span class="line">Method method = class1.getMethod(<span class="string">"info"</span>, String.class);//返回次Class对象对应类的、带指定形参列表的public方法</span><br><span class="line">Method declaredMethod = class1.getDeclaredMethod(<span class="string">"info"</span>, String.class);//返回次Class对象对应类的、带指定形参列表的方法</span><br></pre></td></tr></table></figure></li><li><p>获取class对象的构造函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Constructor&lt;?&gt;[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数</span><br><span class="line">Constructor&lt;?&gt;[] publicConstructors = class1.getConstructors();//获取class对象public构造函数</span><br><span class="line">Constructor&lt;?&gt; constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数</span><br><span class="line">Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数</span><br></pre></td></tr></table></figure></li><li><p>其他方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解</span><br><span class="line">Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解</span><br><span class="line">Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的 Type</span><br><span class="line">Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的<span class="built_in">type</span>集合</span><br></pre></td></tr></table></figure><p>三、 获取class对象的信息<br>比较多</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型</span><br><span class="line">boolean isArray = class1.isArray();//判断是否是集合类</span><br><span class="line">boolean isAnnotation = class1.isAnnotation();//判断是否是注解类</span><br><span class="line">boolean isInterface = class1.isInterface();//判断是否是接口类</span><br><span class="line">boolean isEnum = class1.isEnum();//判断是否是枚举类</span><br><span class="line">boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类</span><br><span class="line">boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰</span><br><span class="line">String className = class1.getName();//获取class名字 包含包名路径</span><br><span class="line">Package aPackage = class1.getPackage();//获取class的包信息</span><br><span class="line">String simpleName = class1.getSimpleName();//获取class类名</span><br><span class="line">int modifiers = class1.getModifiers();//获取class访问权限</span><br><span class="line">Class&lt;?&gt;[] declaredClasses = class1.getDeclaredClasses();//内部类</span><br><span class="line">Class&lt;?&gt; declaringClass = class1.getDeclaringClass();//外部类</span><br></pre></td></tr></table></figure></li></ol><h3 id="通过Java反射生成并操作对象"><a href="#通过Java反射生成并操作对象" class="headerlink" title="通过Java反射生成并操作对象"></a>通过Java反射生成并操作对象</h3><p> 一、生成类的实例对象</p><ol><li><p>使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求该Class对象的对应类有默认构造器，而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。</p></li><li><p>先使用Class对象获取指定的Constructor对象，再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">//第一种方式 Class对象调用newInstance()方法生成</span><br><span class="line">Object obj = class1.newInstance();</span><br><span class="line">//第二种方式 对象获得对应的Constructor对象，再通过该Constructor对象的newInstance()方法生成</span><br><span class="line">Constructor&lt;?&gt; constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数</span><br><span class="line">obj = constructor.newInstance(<span class="string">"hello"</span>);</span><br></pre></td></tr></table></figure></li></ol><p> 二、调用类的方法</p><ol><li><p>通过Class对象的getMethods()方法或者getMethod()方法获得指定方法，返回Method数组或对象。</p></li><li><p>调用Method对象中的Object invoke(Object obj, Object… args)方法。第一个参数对应调用该方法的实例对象，第二个参数对应该方法的参数。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 生成新的对象：用newInstance()方法</span><br><span class="line"> Object obj = class1.newInstance();</span><br><span class="line">//首先需要获得与该方法对应的Method对象</span><br><span class="line">Method method = class1.getDeclaredMethod(<span class="string">"setAge"</span>, int.class);</span><br><span class="line">//调用指定的函数并传递参数</span><br><span class="line">method.invoke(obj, 28);</span><br></pre></td></tr></table></figure></li></ol><p><strong>当通过Method的invoke()方法来调用对应的方法时，Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法，则可以先调用Method对象的如下方法。<br>setAccessible(boolean flag)：将Method对象的acessible设置为指定的布尔值。值为true，指示该Method在使用时应该取消Java语言的访问权限检查；值为false，则知识该Method在使用时要实施Java语言的访问权限检查。</strong></p><p> 三、访问成员变量值</p><ol><li><p>通过Class对象的getFields()方法或者getField()方法获得指定方法，返回Field数组或对象。</p></li><li><p>Field提供了两组方法来读取或设置成员变量的值：<br>getXXX(Object obj):获取obj对象的该成员变量的值。此处的XXX对应8种基本类型。如果该成员变量的类型是引用类型，则取消get后面的XXX。<br>setXXX(Object obj,XXX val)：将obj对象的该成员变量设置成val值。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">//生成新的对象：用newInstance()方法 </span><br><span class="line">Object obj = class1.newInstance();</span><br><span class="line">//获取age成员变量</span><br><span class="line">Field field = class1.getField(<span class="string">"age"</span>);</span><br><span class="line">//将obj对象的age的值设置为10</span><br><span class="line">field.setInt(obj, 10);</span><br><span class="line">//获取obj对象的age的值</span><br><span class="line">field.getInt(obj);</span><br></pre></td></tr></table></figure></li></ol><h3 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h3><p> 定义：<strong>给某个对象提供一个代理对象，并由代理对象控制对于原对象的访问，即客户不直接操控原对象，而是通过代理对象间接地操控原对象。</strong></p><ol><li><p>代理模式的理解<br>代理模式使用代理对象完成用户请求，屏蔽用户对真实对象的访问。现实世界的代理人被授权执行当事人的一些事宜，无需当事人出面，从第三方的角度看，似乎当事人并不存在，因为他只和代理人通信。而事实上代理人是要有当事人的授权，并且在核心问题上还需要请示当事人。<br>在软件设计中，使用代理模式的意图也很多，比如因为安全原因需要屏蔽客户端直接访问真实对象，或者在远程调用中需要使用代理类处理远程方法调用的技术细节，也可能为了提升系统性能，对真实对象进行封装，从而达到延迟加载的目的。</p></li><li><p>代理模式的参与者<br>代理模式的角色分四种：<br><img src="https://camo.githubusercontent.com/1dab04df36af09afb1d73542f9ba66dccc444e8c/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d663464333339613639613862396539322e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="Proxy"><br><strong>主题接口：</strong>Subject 是委托对象和代理对象都共同实现的接口，即代理类的所实现的行为接口。Request() 是委托对象和代理对象共同拥有的方法。<br><strong>目标对象：</strong>ReaSubject 是原对象，也就是被代理的对象。<br><strong>代理对象：</strong>Proxy 是代理对象，用来封装真是主题类的代理类。<br><strong>客户端 ：</strong>使用代理类和主题接口完成一些工作。</p></li><li><p>代理模式的分类<br>代理的实现分为：</p><p><strong>静态代理：</strong>代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。<br><strong>动态代理：</strong>代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件，而是在运行时动态生成的类字节码，并加载到JVM中。</p></li><li><p>代理模式的实现思路<br>a).代理对象和目标对象均实现同一个行为接口。<br>b).代理类和目标类分别具体实现接口逻辑。<br>c).在代理类的构造函数中实例化一个目标对象。<br>d).在代理类中调用目标对象的行为接口。<br>e).客户端想要调用目标对象的行为接口，只能通过代理类来操作。</p></li><li><p>静态代理模式的简单实现</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">public class ProxyDemo &#123;</span><br><span class="line">    public static void main(String args[])&#123;</span><br><span class="line">        RealSubject subject = new RealSubject();</span><br><span class="line">        Proxy p = new Proxy(subject);</span><br><span class="line">        p.request();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">interface Subject&#123;</span><br><span class="line">    void request();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class RealSubject implements Subject&#123;</span><br><span class="line">    public void <span class="function"><span class="title">request</span></span>()&#123;</span><br><span class="line">        System.out.println(<span class="string">"request"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Proxy implements Subject&#123;</span><br><span class="line">    private Subject subject;</span><br><span class="line">    public Proxy(Subject subject)&#123;</span><br><span class="line">        this.subject = subject;</span><br><span class="line">    &#125;</span><br><span class="line">    public void <span class="function"><span class="title">request</span></span>()&#123;</span><br><span class="line">        System.out.println(<span class="string">"PreProcess"</span>);</span><br><span class="line">        subject.request();</span><br><span class="line">        System.out.println(<span class="string">"PostProcess"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>目标对象(RealSubject )以及代理对象（Proxy）都实现了主题接口（Subject）。在代理对象（Proxy）中，通过构造函数传入目标对象(RealSubject )，然后重写主题接口（Subject）的request()方法，在该方法中调用目标对象(RealSubject )的request()方法，并可以添加一些额外的处理工作在目标对象(RealSubject )的request()方法的前后。</p><p><strong>代理模式的好处：</strong><br>假如有这样的需求，要在某些模块方法调用前后加上一些统一的前后处理操作，比如在添加购物车、修改订单等操作前后统一加上登陆验证与日志记录处理，该怎样实现？首先想到最简单的就是直接修改源码，在对应模块的对应方法前后添加操作。如果模块很多，你会发现，修改源码不仅非常麻烦、难以维护，而且会使代码显得十分臃肿。</p><p>这时候就轮到代理模式上场了，它可以在被调用方法前后加上自己的操作，而不需要更改被调用类的源码，大大地降低了模块之间的耦合性，体现了极大的优势。</p><p>静态代理比较简单，上面的简单实例就是静态代理的应用方式，下面介绍本篇文章的主题：动态代理。</p></li></ol><h3 id="Java反射机制与动态代理"><a href="#Java反射机制与动态代理" class="headerlink" title="Java反射机制与动态代理"></a>Java反射机制与动态代理</h3><p> 动态代理的思路和上述思路一致，下面主要讲解如何实现。</p><ol><li><p>动态代理介绍<br>动态代理是指在运行时动态生成代理类。即，代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比，动态类有诸多好处。</p><p>①不需要为(RealSubject )写一个形式上完全一样的封装类，假如主题接口（Subject）中的方法很多，为每一个接口写一个代理方法也很麻烦。如果接口有变动，则目标对象和代理类都要修改，不利于系统维护；<br>②使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑，从而大大提升系统的灵活性。</p></li><li><p>动态代理涉及的主要类<br>主要涉及两个类，这两个类都是java.lang.reflect包下的类，内部主要通过反射来实现的。</p><p><strong>java.lang.reflect.Proxy:</strong>这是生成代理类的主类，通过 Proxy 类生成的代理类都继承了 Proxy 类。<br>Proxy提供了用户创建动态代理类和代理对象的静态方法，它是所有动态代理类的父类。</p><p><strong>java.lang.reflect.InvocationHandler:</strong>这里称他为”调用处理器”，它是一个接口。只有一个invoke方法。当调用动态代理类中的方法时，将会直接转接到执行自定义的InvocationHandler中的invoke()方法。即我们动态生成的代理类需要完成的具体内容需要自己定义一个类，而这个类必须实现 InvocationHandler 接口，通过重写invoke()方法来执行具体内容。<br>InvocationHandler.java代码如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line">package java.lang.reflect;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * &#123;@code InvocationHandler&#125; is the interface implemented by</span><br><span class="line"> * the &lt;i&gt;invocation handler&lt;/i&gt; of a proxy instance.</span><br><span class="line"> *</span><br><span class="line"> * &lt;p&gt;Each proxy instance has an associated invocation handler.</span><br><span class="line"> * When a method is invoked on a proxy instance, the method</span><br><span class="line"> * invocation is encoded and dispatched to the &#123;@code invoke&#125;</span><br><span class="line"> * method of its invocation handler.</span><br><span class="line"> *</span><br><span class="line"> * @author      Peter Jones</span><br><span class="line"> * @see         Proxy</span><br><span class="line"> * @since       1.3</span><br><span class="line"> */</span><br><span class="line">public interface InvocationHandler &#123;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * Processes a method invocation on a proxy instance and returns</span><br><span class="line">     * the result.  This method will be invoked on an invocation handler</span><br><span class="line">     * when a method is invoked on a proxy instance that it is</span><br><span class="line">     * associated with.</span><br><span class="line">     *</span><br><span class="line">     * @param   proxy the proxy instance that the method was invoked on</span><br><span class="line">     *</span><br><span class="line">     * @param   method the &#123;@code Method&#125; instance corresponding to</span><br><span class="line">     * the interface method invoked on the proxy instance.  The declaring</span><br><span class="line">     * class of the &#123;@code Method&#125; object will be the interface that</span><br><span class="line">     * the method was declared <span class="keyword">in</span>, <span class="built_in">which</span> may be a superinterface of the</span><br><span class="line">     * proxy interface that the proxy class inherits the method through.</span><br><span class="line">     *</span><br><span class="line">     * @param   args an array of objects containing the values of the</span><br><span class="line">     * arguments passed <span class="keyword">in</span> the method invocation on the proxy instance,</span><br><span class="line">     * or &#123;@code null&#125; <span class="keyword">if</span> interface method takes no arguments.</span><br><span class="line">     * Arguments of primitive types are wrapped <span class="keyword">in</span> instances of the</span><br><span class="line">     * appropriate primitive wrapper class, such as</span><br><span class="line">     * &#123;@code java.lang.Integer&#125; or &#123;@code java.lang.Boolean&#125;.</span><br><span class="line">     *</span><br><span class="line">     * @<span class="built_in">return</span>  the value to <span class="built_in">return</span> from the method invocation on the</span><br><span class="line">     * proxy instance.  If the declared <span class="built_in">return</span> <span class="built_in">type</span> of the interface</span><br><span class="line">     * method is a primitive <span class="built_in">type</span>, <span class="keyword">then</span> the value returned by</span><br><span class="line">     * this method must be an instance of the corresponding primitive</span><br><span class="line">     * wrapper class; otherwise, it must be a <span class="built_in">type</span> assignable to the</span><br><span class="line">     * declared <span class="built_in">return</span> <span class="built_in">type</span>.  If the value returned by this method is</span><br><span class="line">     * &#123;@code null&#125; and the interface method<span class="string">'s return type is</span></span><br><span class="line"><span class="string">     * primitive, then a &#123;@code NullPointerException&#125; will be</span></span><br><span class="line"><span class="string">     * thrown by the method invocation on the proxy instance.  If the</span></span><br><span class="line"><span class="string">     * value returned by this method is otherwise not compatible with</span></span><br><span class="line"><span class="string">     * the interface method'</span>s declared <span class="built_in">return</span> <span class="built_in">type</span> as described above,</span><br><span class="line">     * a &#123;@code ClassCastException&#125; will be thrown by the method</span><br><span class="line">     * invocation on the proxy instance.</span><br><span class="line">     *</span><br><span class="line">     * @throws  Throwable the exception to throw from the method</span><br><span class="line">     * invocation on the proxy instance.  The exception<span class="string">'s type must be</span></span><br><span class="line"><span class="string">     * assignable either to any of the exception types declared in the</span></span><br><span class="line"><span class="string">     * &#123;@code throws&#125; clause of the interface method or to the</span></span><br><span class="line"><span class="string">     * unchecked exception types &#123;@code java.lang.RuntimeException&#125;</span></span><br><span class="line"><span class="string">     * or &#123;@code java.lang.Error&#125;.  If a checked exception is</span></span><br><span class="line"><span class="string">     * thrown by this method that is not assignable to any of the</span></span><br><span class="line"><span class="string">     * exception types declared in the &#123;@code throws&#125; clause of</span></span><br><span class="line"><span class="string">     * the interface method, then an</span></span><br><span class="line"><span class="string">     * &#123;@link UndeclaredThrowableException&#125; containing the</span></span><br><span class="line"><span class="string">     * exception that was thrown by this method will be thrown by the</span></span><br><span class="line"><span class="string">     * method invocation on the proxy instance.</span></span><br><span class="line"><span class="string">     *</span></span><br><span class="line"><span class="string">     * @see     UndeclaredThrowableException</span></span><br><span class="line"><span class="string">     */</span></span><br><span class="line"><span class="string">    public Object invoke(Object proxy, Method method, Object[] args)</span></span><br><span class="line"><span class="string">        throws Throwable;</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure><p>Proxy提供了如下两个方法来创建动态代理类和动态代理实例。</p><blockquote><p>static Class&lt;?&gt; getProxyClass(ClassLoader loader, Class&lt;?&gt;… interfaces) 返回代理类的java.lang.Class对象。第一个参数是类加载器对象（即哪个类加载器来加载这个代理类到 JVM 的方法区），第二个参数是接口（表明你这个代理类需要实现哪些接口），第三个参数是调用处理器类实例（指定代理类中具体要干什么），该代理类将实现interfaces所指定的所有接口，执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。</p></blockquote></li></ol><blockquote><p>static Object newProxyInstance(ClassLoader loader, Class&lt;?&gt;[] interfaces, InvocationHandler h) 返回代理类实例。参数与上述方法一致。</p></blockquote><p> 对应上述两种方法创建动态代理对象的方式：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">//创建一个InvocationHandler对象</span><br><span class="line">InvocationHandler handler = new MyInvocationHandler(.args..);</span><br><span class="line">//使用Proxy生成一个动态代理类</span><br><span class="line">Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);</span><br><span class="line">//获取proxyClass类中一个带InvocationHandler参数的构造器</span><br><span class="line">Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);</span><br><span class="line">//调用constructor的newInstance方法来创建动态实例</span><br><span class="line">RealSubject real = (RealSubject)constructor.newInstance(handler);</span><br></pre></td></tr></table></figure></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//创建一个InvocationHandler对象</span><br><span class="line">InvocationHandler handler = new MyInvocationHandler(.args..);</span><br><span class="line">//使用Proxy直接生成一个动态代理对象</span><br><span class="line">RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);</span><br></pre></td></tr></table></figure><p> <strong>newProxyInstance这个方法实际上做了两件事：第一，创建了一个新的类【代理类】，这个类实现了Class[] interfaces中的所有接口，并通过你指定的ClassLoader将生成的类的字 节码加载到JVM中，创建Class对象；第二，以你传入的InvocationHandler作为参数创建一个代理类的实例并返回。</strong></p><p> Proxy 类还有一些静态方法，比如：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">InvocationHandler getInvocationHandler(Object proxy):获得代理对象对应的调用处理器对象。</span><br><span class="line"></span><br><span class="line">Class getProxyClass(ClassLoader loader, Class[] interfaces):根据类加载器和实现的接口获得代理类。</span><br></pre></td></tr></table></figure></p><p>InvocationHandler 接口中有方法：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">invoke(Object proxy, Method method, Object[] args)</span><br></pre></td></tr></table></figure></p><p> 这个函数是在代理对象调用任何一个方法时都会调用的，方法不同会导致第二个参数method不同，第一个参数是代理对象（表示哪个代理对象调用了method方法），第二个参数是 Method 对象（表示哪个方法被调用了），第三个参数是指定调用方法的参数。</p><ol start="3"><li><p>动态代理模式的简单实现</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">public class DynamicProxyDemo &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        //1.创建目标对象</span><br><span class="line">        RealSubject realSubject = new RealSubject();    </span><br><span class="line">        //2.创建调用处理器对象</span><br><span class="line">        ProxyHandler handler = new ProxyHandler(realSubject);    </span><br><span class="line">       //3.动态生成代理对象</span><br><span class="line">        Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),</span><br><span class="line">                                                        RealSubject.class.getInterfaces(), handler);   </span><br><span class="line">        //4.通过代理对象调用方法   </span><br><span class="line">        proxySubject.request();    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * 主题接口</span><br><span class="line"> */</span><br><span class="line">interface Subject&#123;</span><br><span class="line">    void request();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * 目标对象类</span><br><span class="line"> */</span><br><span class="line">class RealSubject implements Subject&#123;</span><br><span class="line">    public void <span class="function"><span class="title">request</span></span>()&#123;</span><br><span class="line">        System.out.println(<span class="string">"====RealSubject Request===="</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">/**</span><br><span class="line"> * 代理类的调用处理器</span><br><span class="line"> */</span><br><span class="line">class ProxyHandler implements InvocationHandler&#123;</span><br><span class="line">    private Subject subject;</span><br><span class="line">    public ProxyHandler(Subject subject)&#123;</span><br><span class="line">        this.subject = subject;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public Object invoke(Object proxy, Method method, Object[] args)</span><br><span class="line">            throws Throwable &#123;</span><br><span class="line">        //定义预处理的工作，当然你也可以根据 method 的不同进行不同的预处理工作</span><br><span class="line">        System.out.println(<span class="string">"====before===="</span>);</span><br><span class="line">       //调用RealSubject中的方法</span><br><span class="line">        Object result = method.invoke(subject, args);</span><br><span class="line">        System.out.println(<span class="string">"====after===="</span>);</span><br><span class="line">        <span class="built_in">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，我们通过newProxyInstance就产生了一个Subject 的实例，即代理类的实例，然后就可以通过Subject .request()，就会调用InvocationHandler中的invoke()方法，传入方法Method对象，以及调用方法的参数，通过Method.invoke调用RealSubject中的方法的request()方法。同时可以在InvocationHandler中的invoke()方法加入其他执行逻辑。</p></li></ol><h3 id="泛型和Class类"><a href="#泛型和Class类" class="headerlink" title="泛型和Class类"></a>泛型和Class类</h3><p> 从JDK 1.5 后，Java中引入泛型机制，Class类也增加了泛型功能，从而允许使用泛型来限制Class类，例如：String.class的类型实际上是Class<string>。如果Class对应的类暂时未知，则使用Class&lt;?&gt;(?是通配符)。通过反射中使用泛型，可以避免使用反射生成的对象需要强制类型转换。</string></p><p> 泛型的好处众多，最主要的一点就是避免类型转换，防止出现ClassCastException，即类型转换异常。以下面程序为例：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">public class ObjectFactory &#123;</span><br><span class="line">    public static Object getInstance(String name)&#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            //创建指定类对应的Class对象</span><br><span class="line">            Class cls = Class.forName(name);</span><br><span class="line">            //返回使用该Class对象创建的实例</span><br><span class="line">            <span class="built_in">return</span> cls.newInstance();</span><br><span class="line">        &#125; catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="built_in">return</span> null;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 上面程序是个工厂类，通过指定的字符串创建Class对象并创建一个类的实例对象返回。但是这个对象的类型是Object对象，取出实例后需要强制类型转换。<br> 如下例：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Date date = (Date) ObjectFactory.getInstance(<span class="string">"java.util.Date"</span>);</span><br></pre></td></tr></table></figure></p><p> 又或者如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String string = (String) ObjectFactory.getInstance(<span class="string">"java.util.Date"</span>);</span><br></pre></td></tr></table></figure></p><p> 上面代码在编译时不会有任何问题，但是运行时将抛出ClassCastException异常，因为程序试图将一个Date对象转换成String对象。<br> 但是泛型的出现后，就可以避免这种情况。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class ObjectFactory &#123;</span><br><span class="line">    public static &lt;T&gt; T getInstance(Class&lt;T&gt; cls) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            // 返回使用该Class对象创建的实例</span><br><span class="line">            <span class="built_in">return</span> cls.newInstance();</span><br><span class="line">        &#125; catch (InstantiationException | IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="built_in">return</span> null;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 在上面程序的getInstance()方法中传入一个Class<t>参数，这是一个泛型化的Class对象，调用该Class对象的newInstance()方法将返回一个T对象。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String instance = ObjectFactory.getInstance(String.class);</span><br></pre></td></tr></table></figure></t></p><p> 通过传入String.class便知道T代表String，所以返回的对象是String类型的，避免强制类型转换。<br> 当然Class类引入泛型的好处不止这一点，在以后的实际应用中会更加能体会到。</p><h3 id="使用反射来获取泛型信息"><a href="#使用反射来获取泛型信息" class="headerlink" title="使用反射来获取泛型信息"></a>使用反射来获取泛型信息</h3><p> 通过指定类对应的 Class 对象，可以获得该类里包含的所有 Field，不管该 Field 是使用 private 修饰，还是使用 public 修饰。获得了 Field 对象后，就可以很容易地获得该 Field 的数据类型，即使用如下代码即可获得指定 Field 的类型。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 获取 Field 对象 f 的类型</span><br><span class="line">Class&lt;?&gt; a = f.getType();</span><br></pre></td></tr></table></figure></p><p> 但这种方式只对普通类型的 Field 有效。如果该 Field 的类型是有泛型限制的类型，如 Map&lt;String, Integer&gt; 类型，则不能准确地得到该 Field 的泛型参数。<br> 为了获得指定 Field 的泛型类型，应先使用如下方法来获取指定 Field 的类型。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 获得 Field 实例的泛型类型</span><br><span class="line">Type <span class="built_in">type</span> = f.getGenericType();</span><br></pre></td></tr></table></figure></p><p> 然后将 Type 对象强制类型转换为 ParameterizedType 对象，ParameterizedType 代表被参数化的类型，也就是增加了泛型限制的类型。ParameterizedType 类提供了如下两个方法。<br> <strong>getRawType()：</strong>返回没有泛型信息的原始类型。<br> <strong>getActualTypeArguments()：</strong>返回泛型参数的类型。<br> 下面是一个获取泛型类型的完整程序。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">public class GenericTest</span><br><span class="line">&#123;</span><br><span class="line">    private Map&lt;String , Integer&gt; score;</span><br><span class="line">    public static void main(String[] args)</span><br><span class="line">        throws Exception</span><br><span class="line">    &#123;</span><br><span class="line">        Class&lt;GenericTest&gt; clazz = GenericTest.class;</span><br><span class="line">        Field f = clazz.getDeclaredField(<span class="string">"score"</span>);</span><br><span class="line">        // 直接使用getType()取出Field类型只对普通类型的Field有效</span><br><span class="line">        Class&lt;?&gt; a = f.getType();</span><br><span class="line">        // 下面将看到仅输出java.util.Map</span><br><span class="line">        System.out.println(<span class="string">"score的类型是:"</span> + a);</span><br><span class="line">        // 获得Field实例f的泛型类型</span><br><span class="line">        Type gType = f.getGenericType();</span><br><span class="line">        // 如果gType类型是ParameterizedType对象</span><br><span class="line">        <span class="keyword">if</span>(gType instanceof ParameterizedType)</span><br><span class="line">        &#123;</span><br><span class="line">            // 强制类型转换</span><br><span class="line">            ParameterizedType pType = (ParameterizedType)gType;</span><br><span class="line">            // 获取原始类型</span><br><span class="line">            Type rType = pType.getRawType();</span><br><span class="line">            System.out.println(<span class="string">"原始类型是："</span> + rType);</span><br><span class="line">            // 取得泛型类型的泛型参数</span><br><span class="line">            Type[] tArgs = pType.getActualTypeArguments();</span><br><span class="line">            System.out.println(<span class="string">"泛型类型是:"</span>);</span><br><span class="line">            <span class="keyword">for</span> (int i = 0; i &lt; tArgs.length; i++) </span><br><span class="line">            &#123;</span><br><span class="line">                System.out.println(<span class="string">"第"</span> + i + <span class="string">"个泛型类型是："</span> + tArgs[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">"获取泛型类型出错！"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 输出结果：<br> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">score 的类型是: interface java.util.Map</span><br><span class="line">原始类型是: interface java.util.Map</span><br><span class="line">泛型类型是:</span><br><span class="line">第 0 个泛型类型是: class java.lang.String</span><br><span class="line">第 1 个泛型类型是：class java.lang.Integer</span><br></pre></td></tr></table></figure></p><p> 从上面的运行结果可以看出，直接使用 Field 的 getType() 方法只能获取普通类型的 Field 的数据类型：对于增加了泛型参数的类型的 Field，应该使用 getGenericType() 方法来取得其类型。</p><p> Type 也是 java.lang.reflect 包下的一个接口，该接口代表所有类型的公共高级接口，Class 是 Type 接口的实现类。Type 包括原始类型、参数化类型、数组类型、类型变量和基本类型等。</p><h2 id="Java-I-O"><a href="#Java-I-O" class="headerlink" title="Java I/O"></a>Java I/O</h2><h3 id="字符和字节"><a href="#字符和字节" class="headerlink" title="字符和字节"></a>字符和字节</h3><p> 在Java中有输入、输出两种I/O流，每种输入、输出流又分为字节流和字符流。关于字节，我们在学习8大基本数据类型中都有了解，每个字节(byte)有8bit组成，每种数据类型又有几个字节组成等，关于字符，我们知道代表一个汉字或者英文字母。</p><p> <strong>但是字节和字符之间的关系是怎么样的呢？</strong></p><p> Java采用Unicode编码，2个字节来表示一个字符，这点与C语言不同，C语言采用ASCII码，在大多数系统中，一个字符通常占一个字节，但是在0~127整数之间的字符映射，Unicode向下兼容ASCII。而Java采用unicode来表示字符，一个中文或英文字符的unicode编码都占2个字节。但如果采用其他编码方式，一个字符占用的字节数则各不相同。可能有点晕，举个例子解释下。</p><p> 例如：Java中的String类是按照unicode进行编码的，当使用String(byte[] bytes, String encoding)构造字符串时，encoding所指的是bytes中的数据是按照那种方式编码的，而不是最后产生的String是什么编码方式，换句话说，是让系统把bytes中的数据由encoding编码方式转换成unicode编码。如果不指明，bytes的编码方式将由jdk根据操作系统决定。</p><p> getBytes(String charsetName)使用指定的编码方式将此String编码为 byte 序列，并将结果存储到一个新的 byte 数组中。如果不指定将使用操作系统默认的编码方式，我的电脑默认的是GBK编码。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public class Hel &#123;  </span><br><span class="line">    public static void main(String[] args)&#123;  </span><br><span class="line">        String str = <span class="string">"你好hello"</span>;  </span><br><span class="line">            int byte_len = str.getBytes().length;  </span><br><span class="line">            int len = str.length();  </span><br><span class="line">            System.out.println(<span class="string">"字节长度为："</span> + byte_len);  </span><br><span class="line">        System.out.println(<span class="string">"字符长度为："</span> + len);  </span><br><span class="line">        System.out.println(<span class="string">"系统默认编码方式："</span> + System.getProperty(<span class="string">"file.encoding"</span>));  </span><br><span class="line">       &#125;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 输出结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">字节长度为：9</span><br><span class="line">字符长度为：7</span><br><span class="line">系统默认编码方式：GBK</span><br></pre></td></tr></table></figure></p><p> 这是因为：在 GB 2312 编码或 GBK 编码中，一个英文字母字符存储需要1个字节，一个汉字字符存储需要2个字节。 在UTF-8编码中，一个英文字母字符存储需要1个字节，一个汉字字符储存需要3到4个字节。在UTF-16编码中，一个英文字母字符存储需要2个字节，一个汉字字符储存需要3到4个字节（Unicode扩展区的一些汉字存储需要4个字节）。在UTF-32编码中，世界上任何字符的存储都需要4个字节。</p><p> <strong>简单来讲，一个字符表示一个汉字或英文字母，具体字符与字节之间的大小比例视编码情况而定。有时候读取的数据是乱码，就是因为编码方式不一致，需要进行转换，然后再按照unicode进行编码。</strong></p><h3 id="File类"><a href="#File类" class="headerlink" title="File类"></a>File类</h3><p> File类是java.io包下代表与平台无关的文件和目录，也就是说，如果希望在程序中操作文件或者目录，都可以通过File类来完成。</p><ol><li><p>构造函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">//构造函数File(String pathname)</span><br><span class="line">File f1 =new File(<span class="string">"c:\\abc\\1.txt"</span>);</span><br><span class="line">//File(String parent,String child)</span><br><span class="line">File f2 =new File(<span class="string">"c:\\abc"</span>,<span class="string">"2.txt"</span>);</span><br><span class="line">//File(File parent,String child)</span><br><span class="line">File f3 =new File(<span class="string">"c:"</span>+File.separator+<span class="string">"abc"</span>);//separator 跨平台分隔符</span><br><span class="line">File f4 =new File(f3,<span class="string">"3.txt"</span>);</span><br><span class="line">System.out.println(f1);//c:\abc\1.txt</span><br></pre></td></tr></table></figure><p><strong>路径分隔符：</strong><br>windows： “/“ “” 都可以<br>linux/unix： “/“<br>注意:如果windows选择用””做分割符的话,那么请记得替换成”\”,因为Java中””代表转义字符<br>所以推荐都使用”/“，也可以直接使用代码File.separator，表示跨平台分隔符。<br><strong>路径：</strong><br>相对路径：<br>./表示当前路径<br>../表示上一级路径<br>其中当前路径：默认情况下，java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定，通常是 Java 虚拟机的调用目录。”</p><p>绝对路径：<br>绝对路径名是完整的路径名，不需要任何其他信息就可以定位自身表示的文件</p></li><li><p>创建与删除方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//如果文件存在返回<span class="literal">false</span>，否则返回<span class="literal">true</span>并且创建文件 </span><br><span class="line">boolean createNewFile();</span><br><span class="line">//创建一个File对象所对应的目录，成功返回<span class="literal">true</span>，否则<span class="literal">false</span>。且File对象必须为路径而不是文件。只会创建最后一级目录，如果上级目录不存在就抛异常。</span><br><span class="line">boolean mkdir();</span><br><span class="line">//创建一个File对象所对应的目录，成功返回<span class="literal">true</span>，否则<span class="literal">false</span>。且File对象必须为路径而不是文件。创建多级目录，创建路径中所有不存在的目录</span><br><span class="line">boolean mkdirs()    ;</span><br><span class="line">//如果文件存在返回<span class="literal">true</span>并且删除文件，否则返回<span class="literal">false</span></span><br><span class="line">boolean delete();</span><br><span class="line">//在虚拟机终止时，删除File对象所表示的文件或目录。</span><br><span class="line">void deleteOnExit();</span><br></pre></td></tr></table></figure></li><li><p>判断方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">boolean canExecute()    ;//判断文件是否可执行</span><br><span class="line">boolean canRead();//判断文件是否可读</span><br><span class="line">boolean canWrite();//判断文件是否可写</span><br><span class="line">boolean exists();//判断文件是否存在</span><br><span class="line">boolean isDirectory();//判断是否是目录</span><br><span class="line">boolean isFile();//判断是否是文件</span><br><span class="line">boolean isHidden();//判断是否是隐藏文件或隐藏目录</span><br><span class="line">boolean isAbsolute();//判断是否是绝对路径 文件不存在也能判断</span><br></pre></td></tr></table></figure></li><li><p>获取方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">String getName();//返回文件或者是目录的名称</span><br><span class="line">String getPath();//返回路径</span><br><span class="line">String getAbsolutePath();//返回绝对路径</span><br><span class="line">String getParent();//返回父目录，如果没有父目录则返回null</span><br><span class="line">long lastModified();//返回最后一次修改的时间</span><br><span class="line">long length();//返回文件的长度</span><br><span class="line">File[] listRoots();// 列出所有的根目录（Window中就是所有系统的盘符）</span><br><span class="line">String[] list() ;//返回一个字符串数组，给定路径下的文件或目录名称字符串</span><br><span class="line">String[] list(FilenameFilter filter);//返回满足过滤器要求的一个字符串数组</span><br><span class="line">File[]  listFiles();//返回一个文件对象数组，给定路径下文件或目录</span><br><span class="line">File[] listFiles(FilenameFilter filter);//返回满足过滤器要求的一个文件对象数组</span><br></pre></td></tr></table></figure><p>其中包含了一个重要的接口FileNameFilter，该接口是个文件过滤器，包含了一个accept(File dir,String name)方法，该方法依次对指定File的所有子目录或者文件进行迭代，按照指定条件，进行过滤，过滤出满足条件的所有文件。</p></li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// 文件过滤</span><br><span class="line">    File[] files = file.listFiles(new <span class="function"><span class="title">FilenameFilter</span></span>() &#123;</span><br><span class="line">        @Override</span><br><span class="line">        public boolean accept(File file, String filename) &#123;</span><br><span class="line">            <span class="built_in">return</span> filename.endsWith(<span class="string">".mp3"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure><p> file目录下的所有子文件如果满足后缀是.mp3的条件的文件都会被过滤出来。</p><h3 id="IO流的概念"><a href="#IO流的概念" class="headerlink" title="IO流的概念"></a>IO流的概念</h3><p> Java的IO流是实现输入/输出的基础，它可以方便地实现数据的输入/输出操作，在Java中把不同的输入/输出源抽象表述为”流”。流是一组有顺序的，有起点和终点的字节集合，是对数据传输的总称或抽象。即数据在两设备间的传输称为流，流的本质是数据传输，根据数据传输特性将流抽象为各种类，方便更直观的进行数据操作。<br> <strong>流有输入和输出，输入时是流从数据源流向程序。输出时是流从程序传向数据源，而数据源可以是内存，文件，网络或程序等。</strong></p><p> IO流的分类:</p><ol><li><p>输入流和输出流<br>根据数据流向不同分为：输入流和输出流。</p><blockquote><p>输入流:只能从中读取数据，而不能向其写入数据。<br>输出流：只能向其写入数据，而不能从中读取数据。</p></blockquote></li><li><p>字节流和字符流<br>字节流和字符流和用法几乎完全一样，区别在于字节流和字符流所操作的数据单元不同。<br>字符流的由来： 因为数据编码的不同，而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时，去查了指定的码表。字节流和字符流的区别：<br>（1）读写单位不同：字节流以字节（8bit）为单位，字符流以字符为单位，根据码表映射字符，一次可能读多个字节。<br>（2）处理对象不同：字节流能处理所有类型的数据（如图片、avi等），而字符流只能处理字符类型的数据。</p><p>只要是处理纯文本数据，就优先考虑使用字符流。 除此之外都使用字节流。</p></li><li><p>节点流和处理流<br>按照流的角色来分，可以分为节点流和处理流。<br>可以从/向一个特定的IO设备（如磁盘、网络）读/写数据的流，称为节点流，节点流也被成为低级流。<br>处理流是对一个已存在的流进行连接或封装，通过封装后的流来实现数据读/写功能，处理流也被称为高级流。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//节点流，直接传入的参数是IO设备</span><br><span class="line">FileInputStream fis = new FileInputStream(<span class="string">"test.txt"</span>);</span><br><span class="line">//处理流，直接传入的参数是流对象</span><br><span class="line">BufferedInputStream bis = new BufferedInputStream(fis);</span><br></pre></td></tr></table></figure><p>当使用处理流进行输入/输出时，程序并不会直接连接到实际的数据源，没有和实际的输入/输出节点连接。使用处理流的一个明显好处是，只要使用相同的处理流，程序就可以采用完全相同的输入/输出代码来访问不同的数据源，随着处理流所包装节点流的变化，程序实际所访问的数据源也相应地发生变化。<br>实际上，Java使用处理流来包装节点流是一种典型的装饰器设计模式，通过使用处理流来包装不同的节点流，既可以消除不同节点流的实现差异，也可以提供更方便的方法来完成输入/输出功能。</p></li></ol><h3 id="IO流的四大基类"><a href="#IO流的四大基类" class="headerlink" title="IO流的四大基类"></a>IO流的四大基类</h3><p> 根据流的流向以及操作的数据单元不同，将流分为了四种类型，每种类型对应一种抽象基类。这四种抽象基类分别为：InputStream,Reader,OutputStream以及Writer。四种基类下，对应不同的实现类，具有不同的特性。在这些实现类中，又可以分为节点流和处理流。下面就是整个由着四大基类支撑下，整个IO流的框架图。<br> <img src="https://camo.githubusercontent.com/fdb7d34b4c62c0dde7adca91c766e224ee3e11f3/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f333938353536332d333863336561343536326436646265332e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430" alt="IO流"><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">I/O流</span><br><span class="line">└── 字符流</span><br><span class="line">|       └── Reader</span><br><span class="line">|       |      └── BufferReader</span><br><span class="line">|       |      └── InputStreamReader - FileReader</span><br><span class="line">|       |      └── StringReader</span><br><span class="line">|       |      └── PipedReader</span><br><span class="line">|       |      └── ByteArrayReader</span><br><span class="line">|       |      └── FilterReader - PushBackReader</span><br><span class="line">|       └── Writer</span><br><span class="line">|              └── BufferWriter</span><br><span class="line">|              └── OutputStreamWriter - FileWriter</span><br><span class="line">|              └── StringWriter</span><br><span class="line">|              └── PipedWriter</span><br><span class="line">|              └── CharWriter</span><br><span class="line">|              └── FilterWriter</span><br><span class="line">└── 字节流</span><br><span class="line">        └── InputStream </span><br><span class="line">        |      └── FileInputStream</span><br><span class="line">        |      └── FilterInputStream</span><br><span class="line">        |      |          └── BufferInputStream</span><br><span class="line">        |      |          └── DataInputStream</span><br><span class="line">        |      |          └── PushBackInputStream</span><br><span class="line">        |      └── ObjectInputStream</span><br><span class="line">        |      └── PipedInputStream</span><br><span class="line">        |      └── SequenceInputStream</span><br><span class="line">        |      └── StringBufferInputStream</span><br><span class="line">        |      └── ByteArrayInputStream</span><br><span class="line">        └── OutputStream</span><br><span class="line">               └── FileOutputStream</span><br><span class="line">               └── FilterOutputStream</span><br><span class="line">               |          └── BufferOutputStream</span><br><span class="line">               |          └── DataOutputStream</span><br><span class="line">               |          └── PrintOutputStream</span><br><span class="line">               └── ObjectOutputStream</span><br><span class="line">               └── PipedOutputStream</span><br><span class="line">               └── ByteArrayOutputStream</span><br></pre></td></tr></table></figure></p><p> InputStream,Reader,OutputStream以及Writer，这四大抽象基类，本身并不能创建实例来执行输入/输出，但它们将成为所有输入/输出流的模版，所以它们的方法是所有输入/输出流都可以使用的方法。类似于集合中的Collection接口。</p><ol><li><p>InputStream<br>InputStream 是所有的输入字节流的父类，它是一个抽象类，主要包含三个方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">//读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 </span><br><span class="line">int <span class="built_in">read</span>() ； </span><br><span class="line">//读取一系列字节并存储到一个数组buffer，返回实际读取的字节数，如果读取前已到输入流的末尾返回-1。 </span><br><span class="line">int <span class="built_in">read</span>(byte[] buffer) ； </span><br><span class="line">//读取length个字节并存储到一个字节数组buffer，从off位置开始存,最多len， 返回实际读取的字节数，如果读取前以到输入流的末尾返回-1。 </span><br><span class="line">int <span class="built_in">read</span>(byte[] buffer, int off, int len) ；</span><br></pre></td></tr></table></figure></li><li><p>Reader<br>Reader 是所有的输入字符流的父类，它是一个抽象类，主要包含三个方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">//读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 </span><br><span class="line">int <span class="built_in">read</span>() ； </span><br><span class="line">//读取一系列字符并存储到一个数组buffer，返回实际读取的字符数，如果读取前已到输入流的末尾返回-1。 </span><br><span class="line">int <span class="built_in">read</span>(char[] cbuf) ； </span><br><span class="line">//读取length个字符,并存储到一个数组buffer，从off位置开始存,最多读取len，返回实际读取的字符数，如果读取前以到输入流的末尾返回-1。 </span><br><span class="line">int <span class="built_in">read</span>(char[] cbuf, int off, int len)</span><br></pre></td></tr></table></figure><p>对比InputStream和Reader所提供的方法，就不难发现两个基类的功能基本一样的，只不过读取的数据单元不同。</p><p><strong>在执行完流操作后，要调用close()方法来关系输入流，因为程序里打开的IO资源不属于内存资源，垃圾回收机制无法回收该资源，所以应该显式关闭文件IO资源。</strong></p><p>除此之外，InputStream和Reader还支持如下方法来移动流中的指针位置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">//在此输入流中标记当前的位置</span><br><span class="line">//readlimit - 在标记位置失效前可以读取字节的最大限制。</span><br><span class="line">void mark(int readlimit)</span><br><span class="line">// 测试此输入流是否支持 mark 方法</span><br><span class="line">boolean markSupported()</span><br><span class="line">// 跳过和丢弃此输入流中数据的 n 个字节/字符</span><br><span class="line">long skip(long n)</span><br><span class="line">//将此流重新定位到最后一次对此输入流调用 mark 方法时的位置</span><br><span class="line">void reset()</span><br></pre></td></tr></table></figure></li><li><p>OutputStream<br>OutputStream 是所有的输出字节流的父类，它是一个抽象类，主要包含如下四个方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">//向输出流中写入一个字节数据,该字节数据为参数b的低8位。 </span><br><span class="line">void write(int b) ; </span><br><span class="line">//将一个字节类型的数组中的数据写入输出流。 </span><br><span class="line">void write(byte[] b); </span><br><span class="line">//将一个字节类型的数组中的从指定位置（off）开始的,len个字节写入到输出流。 </span><br><span class="line">void write(byte[] b, int off, int len); </span><br><span class="line">//将输出流中缓冲的数据全部写出到目的地。 </span><br><span class="line">void flush();</span><br></pre></td></tr></table></figure></li><li><p>Writer<br>Writer 是所有的输出字符流的父类，它是一个抽象类,主要包含如下六个方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">//向输出流中写入一个字符数据,该字节数据为参数b的低16位。 </span><br><span class="line">void write(int c); </span><br><span class="line">//将一个字符类型的数组中的数据写入输出流， </span><br><span class="line">void write(char[] cbuf) </span><br><span class="line">//将一个字符类型的数组中的从指定位置（offset）开始的,length个字符写入到输出流。 </span><br><span class="line">void write(char[] cbuf, int offset, int length); </span><br><span class="line">//将一个字符串中的字符写入到输出流。 </span><br><span class="line">void write(String string); </span><br><span class="line">//将一个字符串从offset开始的length个字符写入到输出流。 </span><br><span class="line">void write(String string, int offset, int length); </span><br><span class="line">//将输出流中缓冲的数据全部写出到目的地。 </span><br><span class="line">void flush();</span><br></pre></td></tr></table></figure><p>可以看出，Writer比OutputStream多出两个方法，主要是支持写入字符和字符串类型的数据。</p><p><strong>使用Java的IO流执行输出时，不要忘记关闭输出流，关闭输出流除了可以保证流的物理资源被回收之外，还能将输出流缓冲区的数据flush到物理节点里（因为在执行close()方法之前，自动执行输出流的flush()方法）</strong></p><p>以上内容就是整个IO流的框架介绍。</p></li></ol><h2 id="RandomAccessFile"><a href="#RandomAccessFile" class="headerlink" title="RandomAccessFile"></a>RandomAccessFile</h2><h3 id="RandomAccessFile概述"><a href="#RandomAccessFile概述" class="headerlink" title="RandomAccessFile概述"></a>RandomAccessFile概述</h3><p> RandomAccessFile既可以读取文件内容，也可以向文件输出数据。同时，RandomAccessFile支持“随机访问”的方式，程序快可以直接跳转到文件的任意地方来读写数据。</p><p> 由于RandomAccessFile可以自由访问文件的任意位置，<strong>所以如果需要访问文件的部分内容，而不是把文件从头读到尾，使用RandomAccessFile将是更好的选择。</strong></p><p> 与OutputStream、Writer等输出流不同的是，RandomAccessFile允许自由定义文件记录指针，RandomAccessFile可以不从开始的地方开始输出，因此RandomAccessFile可以向已存在的文件后追加内容。<strong>如果程序需要向已存在的文件后追加内容，则应该使用RandomAccessFile。</strong></p><p> RandomAccessFile的方法虽然多，但它有一个最大的局限，<strong>就是只能读写文件，不能读写其他IO节点。</strong></p><p> RandomAccessFile的一个<strong>重要使用场景就是网络请求中的多线程下载及断点续传。</strong></p><h3 id="RandomAccessFile中的方法"><a href="#RandomAccessFile中的方法" class="headerlink" title="RandomAccessFile中的方法"></a>RandomAccessFile中的方法</h3><ol><li><p>RandomAccessFile的构造函数<br>RandomAccessFile类有两个构造函数，其实这两个构造函数基本相同，只不过是指定文件的形式不同——一个需要使用String参数来指定文件名，一个使用File参数来指定文件本身。除此之外，创建RandomAccessFile对象时还需要指定一个mode参数，该参数指定RandomAccessFile的访问模式，一共有4种模式。</p><blockquote><p>“r”: 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。<br>“rw”: 打开以便读取和写入。<br>“rws”: 打开以便读取和写入。相对于 “rw”，”rws” 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。<br>“rwd” : 打开以便读取和写入，相对于 “rw”，”rwd” 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。</p></blockquote></li><li><p>RandomAccessFile的重要方法</p><p>RandomAccessFile既可以读文件，也可以写文件，所以类似于InputStream的read()方法，以及类似于OutputStream的write()方法，RandomAccessFile都具备。除此之外，RandomAccessFile具备两个特有的方法，来支持其随机访问的特性。</p><p>RandomAccessFile对象包含了一个记录指针，用以标识当前读写处的位置，当程序新创建一个RandomAccessFile对象时，该对象的文件指针记录位于文件头（也就是0处），当读/写了n个字节后，文件记录指针将会后移n个字节。除此之外，RandomAccessFile还可以自由移动该记录指针。下面就是RandomAccessFile具有的两个特殊方法，来操作记录指针，实现随机访问：</p><blockquote><p>long getFilePointer( )：返回文件记录指针的当前位置<br>void seek(long pos )：将文件指针定位到pos位置</p></blockquote></li></ol><h3 id="RandomAccessFile的使用"><a href="#RandomAccessFile的使用" class="headerlink" title="RandomAccessFile的使用"></a>RandomAccessFile的使用</h3><p> 利用RandomAccessFile实现文件的多线程下载，即多线程下载一个文件时，将文件分成几块，每块用不同的线程进行下载。下面是一个利用多线程在写文件时的例子，其中预先分配文件所需要的空间，然后在所分配的空间中进行分块，然后写入：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">/** </span><br><span class="line"> * 测试利用多线程进行文件的写操作 </span><br><span class="line"> */  </span><br><span class="line">public class Test &#123;  </span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws Exception &#123;  </span><br><span class="line">        // 预分配文件所占的磁盘空间，磁盘中会创建一个指定大小的文件  </span><br><span class="line">        RandomAccessFile raf = new RandomAccessFile(<span class="string">"D://abc.txt"</span>, <span class="string">"rw"</span>);  </span><br><span class="line">        raf.setLength(1024*1024); // 预分配 1M 的文件空间  </span><br><span class="line">        raf.close();  </span><br><span class="line"></span><br><span class="line">        // 所要写入的文件内容  </span><br><span class="line">        String s1 = <span class="string">"第一个字符串"</span>;  </span><br><span class="line">        String s2 = <span class="string">"第二个字符串"</span>;  </span><br><span class="line">        String s3 = <span class="string">"第三个字符串"</span>;  </span><br><span class="line">        String s4 = <span class="string">"第四个字符串"</span>;  </span><br><span class="line">        String s5 = <span class="string">"第五个字符串"</span>;  </span><br><span class="line"></span><br><span class="line">        // 利用多线程同时写入一个文件  </span><br><span class="line">        new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据  </span><br><span class="line">        new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据  </span><br><span class="line">        new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据  </span><br><span class="line">        new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据  </span><br><span class="line">        new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    // 利用线程在文件的指定位置写入指定数据  </span><br><span class="line">    static class FileWriteThread extends Thread&#123;  </span><br><span class="line">        private int skip;  </span><br><span class="line">        private byte[] content;  </span><br><span class="line"></span><br><span class="line">        public FileWriteThread(int skip,byte[] content)&#123;  </span><br><span class="line">            this.skip = skip;  </span><br><span class="line">            this.content = content;  </span><br><span class="line">        &#125;  </span><br><span class="line"></span><br><span class="line">        public void <span class="function"><span class="title">run</span></span>()&#123;  </span><br><span class="line">            RandomAccessFile raf = null;  </span><br><span class="line">            try &#123;  </span><br><span class="line">                raf = new RandomAccessFile(<span class="string">"D://abc.txt"</span>, <span class="string">"rw"</span>);  </span><br><span class="line">                raf.seek(skip);  </span><br><span class="line">                raf.write(content);  </span><br><span class="line">            &#125; catch (FileNotFoundException e) &#123;  </span><br><span class="line">                e.printStackTrace();  </span><br><span class="line">            &#125; catch (IOException e) &#123;  </span><br><span class="line">                // TODO Auto-generated catch block  </span><br><span class="line">                e.printStackTrace();  </span><br><span class="line">            &#125; finally &#123;  </span><br><span class="line">                try &#123;  </span><br><span class="line">                    raf.close();  </span><br><span class="line">                &#125; catch (Exception e) &#123;  </span><br><span class="line">                &#125;  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125;  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>当RandomAccessFile向指定文件中插入内容时，将会覆盖掉原有内容。如果不想覆盖掉，则需要将原有内容先读取出来，然后先把插入内容插入后再把原有内容追加到插入内容后。</strong></p><h2 id="Java-NIO"><a href="#Java-NIO" class="headerlink" title="Java NIO"></a>Java NIO</h2><h3 id="NIO-概述"><a href="#NIO-概述" class="headerlink" title="NIO 概述"></a>NIO 概述</h3><p> Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java1.4开始)，Java NIO提供了与标准IO不同的IO工作方式。</p><p> 所以Java NIO是一种新式的IO标准，与之间的普通IO的工作方式不同。标准的IO基于字节流和字符流进行操作的，而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作，数据总是从通道读取到缓冲区中，或者从缓冲区写入通道也类似。</p><p> <strong>由上面的定义就说明NIO是一种新型的IO，但NIO不仅仅就是等于Non-blocking IO（非阻塞IO），NIO中有实现非阻塞IO的具体类，但不代表NIO就是Non-blocking IO（非阻塞IO）。</strong></p><p> Java NIO 由以下几个核心部分组成：</p><ul><li>Buffer</li><li>Channel</li><li>Selector<br>传统的IO操作面向数据流，意味着每次从流中读一个或多个字节，直至完成，数据没有被缓存在任何地方。NIO操作面向缓冲区，数据从Channel读取到Buffer缓冲区，随后在Buffer中处理数据。</li></ul><h3 id="Buffer的使用"><a href="#Buffer的使用" class="headerlink" title="Buffer的使用"></a>Buffer的使用</h3><p> 利用Buffer读写数据，通常遵循四个步骤：</p><ul><li>把数据写入buffer；</li><li>调用flip；</li><li>从Buffer中读取数据；</li><li>调用buffer.clear()</li></ul><p>未完待续</p><h2 id="Java-异常详解"><a href="#Java-异常详解" class="headerlink" title="Java 异常详解"></a>Java 异常详解</h2><h3 id="Java异常概述"><a href="#Java异常概述" class="headerlink" title="Java异常概述"></a>Java异常概述</h3><p> Java异常是Java提供的一种识别及响应错误的一致性机制。</p><p> Java异常机制可以使程序中异常处理代码和正常业务代码分离，保证程序代码更加优雅，并提高程序健壮性。在有效使用异常的情况下，异常能清晰的回答what, where, why这3个问题：异常类型回答了“什么”被抛出，异常堆栈跟踪回答了“在哪“抛出，异常信息回答了“为什么“会抛出。</p><p> Java异常机制用到的几个关键字：try、catch、finally、throw、throws。</p><p>  • try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内，当try语句块内发生异常时，异常就被抛出。</p><p>  • catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。</p><p>  • finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物理资源(如数据库连接、网络连接和磁盘文件)。只有finally块，执行完成之后，才会回来执行try或者catch块中的return或者throw语句，如果finally中使用了return或者throw等终止方法的语句，则就不会跳回执行，直接停止。</p><p>  • throw – 用于抛出异常。</p><p>  • throws – 用在方法签名中，用于声明该方法可能抛出的异常。</p><p> 下面通过几个示例对这几个关键字进行简单了解。<br> 示例一: 了解try和catch基本用法<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">public class Demo1 &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            int i = 10/0;</span><br><span class="line">              System.out.println(<span class="string">"i="</span>+i); </span><br><span class="line">        &#125; catch (ArithmeticException e) &#123;</span><br><span class="line">              System.out.println(<span class="string">"Caught Exception"</span>); </span><br><span class="line">            System.out.println(<span class="string">"e.getMessage(): "</span> + e.getMessage()); </span><br><span class="line">            System.out.println(<span class="string">"e.toString(): "</span> + e.toString()); </span><br><span class="line">            System.out.println(<span class="string">"e.printStackTrace():"</span>);</span><br><span class="line">            e.printStackTrace(); </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Caught Exception</span><br><span class="line">e.getMessage(): / by zero</span><br><span class="line">e.toString(): java.lang.ArithmeticException: / by zero</span><br><span class="line">e.printStackTrace():</span><br><span class="line">java.lang.ArithmeticException: / by zero</span><br><span class="line">    at Demo1.main(Demo1.java:6)</span><br></pre></td></tr></table></figure></p><p> 结果说明：在try语句块中有除数为0的操作，该操作会抛出java.lang.ArithmeticException异常。通过catch，对该异常进行捕获。</p><p> 观察结果我们发现，并没有执行System.out.println(“i=”+i)。这说明try语句块发生异常之后，try语句块中的剩余内容就不会再被执行了。</p><p> 示例二: 了解finally的基本用法,在”示例一”的基础上，我们添加finally语句。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public class Demo2 &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            int i = 10/0;</span><br><span class="line">              System.out.println(<span class="string">"i="</span>+i); </span><br><span class="line">        &#125; catch (ArithmeticException e) &#123;</span><br><span class="line">              System.out.println(<span class="string">"Caught Exception"</span>); </span><br><span class="line">            System.out.println(<span class="string">"e.getMessage(): "</span> + e.getMessage()); </span><br><span class="line">            System.out.println(<span class="string">"e.toString(): "</span> + e.toString()); </span><br><span class="line">            System.out.println(<span class="string">"e.printStackTrace():"</span>);</span><br><span class="line">            e.printStackTrace(); </span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            System.out.println(<span class="string">"run finally"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">Caught Exception</span><br><span class="line">e.getMessage(): / by zero</span><br><span class="line">e.toString(): java.lang.ArithmeticException: / by zero</span><br><span class="line">e.printStackTrace():</span><br><span class="line">java.lang.ArithmeticException: / by zero</span><br><span class="line">    at Demo2.main(Demo2.java:6)</span><br><span class="line">run finally</span><br><span class="line">``` </span><br><span class="line"> 结果说明：最终执行了finally语句块。</span><br><span class="line"></span><br><span class="line"> 示例三: 了解throws和throw的基本用法</span><br><span class="line"> **throws**是**用于声明抛出的异常**，而**throw**是**用于抛出异常**。</span><br><span class="line">``` bash</span><br><span class="line">class MyException extends Exception &#123;</span><br><span class="line">    public <span class="function"><span class="title">MyException</span></span>() &#123;&#125;</span><br><span class="line">    public MyException(String msg) &#123;</span><br><span class="line">        super(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class Demo3 &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            <span class="built_in">test</span>();</span><br><span class="line">        &#125; catch (MyException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">"Catch My Exception"</span>);</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public static void <span class="built_in">test</span>() throws MyException&#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            int i = 10/0;</span><br><span class="line">              System.out.println(<span class="string">"i="</span>+i); </span><br><span class="line">        &#125; catch (ArithmeticException e) &#123;</span><br><span class="line">            throw new MyException(<span class="string">"This is MyException"</span>); </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p> 运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Catch My Exception</span><br><span class="line">MyException: This is MyException</span><br><span class="line">    at Demo3.test(Demo3.java:24)</span><br><span class="line">    at Demo3.main(Demo3.java:13)</span><br></pre></td></tr></table></figure></p><p> 运行结果说明：MyException是继承于Exception的子类。test()的try语句块中产生ArithmeticException异常(除数为0)，并在catch中捕获该异常；接着抛出MyException异常。main()方法对test()中抛出的MyException进行捕获处理。</p><h3 id="Java异常框架"><a href="#Java异常框架" class="headerlink" title="Java异常框架"></a>Java异常框架</h3><p>未完待续</p><h2 id="Java抽象类和接口的区别"><a href="#Java抽象类和接口的区别" class="headerlink" title="Java抽象类和接口的区别"></a>Java抽象类和接口的区别</h2><p> 未完待续</p><h2 id="Java深拷贝和浅拷贝"><a href="#Java深拷贝和浅拷贝" class="headerlink" title="Java深拷贝和浅拷贝"></a>Java深拷贝和浅拷贝</h2><p>未完待续</p><h2 id="Java中transient关键字"><a href="#Java中transient关键字" class="headerlink" title="Java中transient关键字"></a>Java中transient关键字</h2><p>未完待续</p><h2 id="Java中finally和return的执行顺序"><a href="#Java中finally和return的执行顺序" class="headerlink" title="Java中finally和return的执行顺序"></a>Java中finally和return的执行顺序</h2><p>未完代码</p><h2 id="Java-8-新特性"><a href="#Java-8-新特性" class="headerlink" title="Java 8 新特性"></a>Java 8 新特性</h2><h2 id="Java-8-源码"><a href="#Java-8-源码" class="headerlink" title="Java 8 源码"></a>Java 8 源码</h2><p><a href="https://github.com/EricChows/JDK-1.8-sourcecode" target="_blank" rel="noopener">JDk 1.8 Source Code</a></p><h1 id="Java-并发"><a href="#Java-并发" class="headerlink" title="Java 并发"></a>Java 并发</h1><p> 本部分内容是关于Java并发的一些知识总结，既是学习的难点，同时也是面试中几乎必问的知识点。</p><p> 面试中可能会问的一些问题：<br> • 创建线程的方式<br> • Synchronized/ReentrantLock<br> • 生产者/消费者模式<br> • volatile关键字<br> • 乐观锁/悲观锁<br> • 死锁<br> • 了解的并发集合</p><h2 id="Java创建线程的三种方式"><a href="#Java创建线程的三种方式" class="headerlink" title="Java创建线程的三种方式"></a>Java创建线程的三种方式</h2><h2 id="Java线程池"><a href="#Java线程池" class="headerlink" title="Java线程池"></a>Java线程池</h2><h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h2><h2 id="Synchronize-Reentrantlock"><a href="#Synchronize-Reentrantlock" class="headerlink" title="Synchronize/Reentrantlock"></a>Synchronize/Reentrantlock</h2><h2 id="生产者消费者模式"><a href="#生产者消费者模式" class="headerlink" title="生产者消费者模式"></a>生产者消费者模式</h2><hr><h1 id="Android基础-1"><a href="#Android基础-1" class="headerlink" title="Android基础"></a>Android基础</h1><p> 主要复习Android的一些个基础知识点</p><ul><li>四大组件</li><li>事件分发机制</li><li>消息机制</li><li>binder机制</li><li>线程和进程</li></ul><h2 id="Activity全方位解析"><a href="#Activity全方位解析" class="headerlink" title="Activity全方位解析"></a>Activity全方位解析</h2><p> 先从Activity的功能上复习</p><p> MyQuestion:</p><ol><li>能否从Framework的层面来分析一下，Activity这样设计的原理和机制是什么？</li></ol><ol start="2"><li>如何从Framework代码中看出Activity的设计逻辑</li></ol><h3 id="Activity生命周期"><a href="#Activity生命周期" class="headerlink" title="Activity生命周期"></a>Activity生命周期</h3><ol><li><p>Activity生命周期<br>典型的生命周期<br>onCreate -&gt; onRestart -&gt; onStart -&gt; onResume -&gt; onPause -&gt; onStop -&gt; onDestory<br>onCreate:加载布局资源，初始化Activity所需要的数据，<br>onRestart:从不可见变为可见<br>onStart:表示Activity正在被启动，此时Activity已经出现，但是还没有出现在前台，无法与用户交互，这个时候可以理解为Activity已经显示出来，但是我们看不到。<br>onResume:表示Activity已经可见，且出现在前台<br>onPause:表示Activity正在停止，但是仍可见，正常情况下，紧接着onStop就会被调用。特殊情况下，如果这个时候快速的返回当前Activity，那么onResume就会被调用(极端情况)<br>onStop:表示Activity即将停止，不可见，位于后台，可以做稍微重量级的回收工作，同样不能太耗时。<br>onDestory:表示Activity即将被销毁，这是Activity的最后一个回调，可以做一些回收工作和最终的资源回收。、</p></li><li><p>特殊情况下的生命周期<br>a) 横竖屏切换<br>横竖屏切换的过程中，会发生Activity被销毁和重建的过程。<br>onSaveInstanceState和onRestoreInstanceState</p><p>onSaveInstanceState在onStop之前被调用<br>onRestoreInstanceState在onStart之后调用</p><p>b)资源不足导致优先级低的Activity被杀死</p></li></ol><h3 id="Activity的启动模式"><a href="#Activity的启动模式" class="headerlink" title="Activity的启动模式"></a>Activity的启动模式</h3><p> 从Framework中Activity相关代码可以看到，Activity的管理是通过ActivityStack任务栈的形式来管理。<br> Activity的LaunchMode主要有四种：</p><ol><li>Standard 标准模式：每次启动Activity，都会new一个新的Activity放在栈顶</li><li>SingleTop 栈顶复用：栈顶存在，则直接使用，不用new</li><li>SingleTask 栈内复用</li><li><p>SingleInstance 单例模式</p><p>特殊情况：前台栈和后台栈交互</p></li></ol><h3 id="Activity的Flag"><a href="#Activity的Flag" class="headerlink" title="Activity的Flag"></a>Activity的Flag</h3><p> FLAG_ACTIVITY_NEW_TASK</p><p> FLAG_ACTIVITY_SINGLE_TOP</p><p> FLAG_ACTIVITY_CLEAR_TOP</p><h2 id="Service-全方位解析"><a href="#Service-全方位解析" class="headerlink" title="Service 全方位解析"></a>Service 全方位解析</h2><p> Service是ANdroid中实现程序后台运行的解决方案，它非常适用于执行那些不需要和用户交互而且还要求长期运行的任务。Servcie默认并不会运行在子线程中，它也不运行在一个独立的进程中，它同样执行在UI线程中，因此，不要在Service中执行耗时的操作，除非你在Service中创建子线程来完成耗时操作。</p><p> MyQuestion：</p><ol><li>在Android Framework代码中，Service的机制是什么?</li></ol><ol><li><p>Service种类<br>按运行类型分类：</p><pre><code>区别                                    应用</code></pre><p>前台服务   会在通知栏显示onGoing的Notification     服务被终止时，通知栏Notification也会消失，如：音乐播放服务<br>后台服务   默认的服务即为后台服务，不会在通知栏显示  服务被终止时，用户看不到，如：天气更新、日期同步、邮件同步</p><p>按使用方式分类：<br>  类别                          区别<br>startServcie启动的服务       主要用于启动一个服务咨询后台任务，不进行通讯，停止服务，使用stopService<br>bindService启动的服务        可以进行进程通讯.停止服务，使用unbindService<br>同时使用StartServcie和bindService启动的服务    停止服务使用stopService和unbindServcie</p></li><li><p>Service生命周期<br>onCreate -&gt; onStartCommand -&gt; onDestory<br>onCreate -&gt; onBind -&gt; onUnbind -&gt; onDestory<br>onCreate:系统在Service第一次创建时执行此方法，如果service已经运行，此方法不会调用<br>onStartCommand:每次客户端调用startService方法启动该service，都会回调该方法<br>onBind:当调用bindService时，执行该方法，一次调用</p></li></ol><h2 id="BroadcastReceiver全方位解析"><a href="#BroadcastReceiver全方位解析" class="headerlink" title="BroadcastReceiver全方位解析"></a>BroadcastReceiver全方位解析</h2><p> BroadcastReceiver广播接收器，使用了观察者模式来实现。<br> 模型中有三个角色：<br> a)消息订阅者 –广播接收者<br> b)消息发布者 –广播发送者<br> c)消息中心(AMS,即ActivityManagerService)</p><p> 原理描述：<br> i) 广播接受者通过Binder机制在AMS注册<br> ii)广播发送者通过Binder机制向AMS发送广播<br> iii)AMS根据广播发送者要求，在已注册列表中，寻求合适的广播接收者</p><ul><li><p>寻找依据：IntenFilter/Permission<br>iv)AMS将广播发送到合适的广播接收者相应的消息循环队列中<br>v)广播接收者通过消息循环 拿到此广播，并回调onReceive()</p><p>特别注意：广播发送者和广播接收者执行的是 异步的，发出去的广播不会关心有无接收者接收，也不确定接收者到底何时才能接收到。</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/broadcastreceiver.md" target="_blank" rel="noopener">参考文章</a></p><p>My Question:<br>需要拿出AMS的源码来详细的分析原理</p></li></ul><h2 id="ContenProvider全方位解析"><a href="#ContenProvider全方位解析" class="headerlink" title="ContenProvider全方位解析"></a>ContenProvider全方位解析</h2><ol><li><p>作用：进程间 数据交互和共享，跨进程通讯<br>ContentProvider只是中间者角色，真正存储&amp;操作数据的数据源还是原来存储数据的方式(数据库、文件、xml、网络)</p></li><li><p>原理：ContentProvider 采用的是Android中Binder机制来实现</p></li><li><p>使用</p></li></ol><ul><li>URL 统一资源标识符</li><li>MIME 数据类型</li><li>ContentProvider类的 使用方法和数据组织方式</li><li><p>辅助工具类：a)ContentResolver类  b)ContentUris 类 c)UriMatcher类  d) ContentObserver类</p><p>3.1 统一资源标识符<br>Uniform Resource Identifier<br>作用：唯一标识ContentProvider 其中的数据<br>具体使用，分两类，系统预制和自定义</p><p>自定义URI = content://com.carson.provider/User/1<br>主题(Schema):URI的前缀，由Android规定<br>授权信息(Authority):唯一标识符，一般是包名<br>表名(Path):指向数据库中的某个表<br>记录(ID):表中的某个记录(若无指定，则返回全部记录)</p><p>3.2 MIME数据类型</p><p>3.3 ContentProvider 类</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/ContentProvider.md" target="_blank" rel="noopener">参考文章</a></p></li></ul><h2 id="Fragment-详解"><a href="#Fragment-详解" class="headerlink" title="Fragment 详解"></a>Fragment 详解</h2><ol><li><p>什么是Fragment<br>简单来说，就是显示在Activity中的Activity</p></li><li><p>Fragment的生命周期<br>由于Fragment是依附于Activity存在的，因此，它的生命周期受到Activity的生命周期的影响<br>onCreate(){ onAttach() -&gt; onCreate() -&gt; onCreateView() -&gt; onActivityCreated() }<br>onStart(){ onStart() }<br>onResume(){ onResume() }<br>onPause(){ onPause() }<br>onStop(){ onStop() }<br>onDestory(){ onDestoryView() -&gt; onDestory() -&gt; onDetach() }</p><p>PS： 注意：除了onCreateView,其他的所有方法如果重写了，必须调用父类对于该方法的实现</p></li><li><p>Fragment的使用方式<br>静态使用Fragment</p><p>动态使用Fragment</p></li><li><p>什么是Fragment的回退栈？<br>Fragment的回退栈是用来保存每一次Fragment事务发生的变化，如果你将Fragment任务添加到回退栈，当用户点击回退按钮时，将看到上一次保存的Fragment。一旦Fragment完全从回退栈弹出，用户再次点击后退键，则退出当前Activity</p><p>FragmentTransaction.addToBackStack(String)</p></li><li><p>Fragment和Activity之间的通信</p></li><li><p>Fragment与Activity通信的优化</p></li><li><p>如何处理运行时配置发生变化</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/Fragment.md" target="_blank" rel="noopener">参考文章</a></p></li></ol><h2 id="Android消息机制"><a href="#Android消息机制" class="headerlink" title="Android消息机制"></a>Android消息机制</h2><p>  Android中的消息机制，即Handler.</p><ol><li><p>消息机制的模型<br>消息机制主要包含：MessageQueue/Handler/Looper/Message四个模块，现在来一一介绍</p><p><strong>Message:</strong> 需要传递的消息，可以装载数据<br><strong>MessageQueue:</strong> 消息队列，它的内部实现并不是用的队列，实际是通过一个单链表的数据结构来维护消息列表，因为单链表在插入和删除上比较有优势。主要功能是向消息池投递消息<br>(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next). <a href="/home/zds/Code/Junior/frameworks/base/core/java/android/os/MessageQueue.java">MessageQueue</a><br><strong>Handler:</strong> 消息辅助类，主要功能是向信息池发送各种消息事件(Handler.sendMessage)和取走处理相应消息事件(Handler.handleMessage).<br><strong>Looper：</strong>不断循环执行(Looper.loop),从MessageQueue中读取消息，按分发机制将消息分发给目标处理.</p></li><li><p>消息机制的架构</p></li></ol><ol start="3"><li><p>消息机制原理<br>frameworks/base/core/java/android/os/MessageQueue.java<br>frameworks/base/core/java/android/os/Message.java<br>frameworks/base/core/java/android/os/Message.aidl<br>frameworks/base/core/java/android/os/Looper.java<br>frameworks/base/core/java/android/os/Handler.java</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/message-mechanism.md" target="_blank" rel="noopener">参考文章</a></p></li></ol><h2 id="Android事件分发机制"><a href="#Android事件分发机制" class="headerlink" title="Android事件分发机制"></a>Android事件分发机制</h2><ol><li><p>事件在哪些对象间进行传递<br>Activity、ViewGroup、View，事件产生后，传递顺序：Activity &gt; ViewGroup &gt; View</p></li><li><p>事件分发的本质，就是Activity/ViewGroup/View三者的事件分发<br>参考资料2中有对源码进行分析</p><p>参考资料[1]:Myblog中的 《Android编程下Touch事件的分发和消费机制》</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/Event-Dispatch.md" target="_blank" rel="noopener">参考资料2</a> </p></li><li><p>onTouch和onTouchEvent的区别</p></li><li><p>Touch事件的后续(Move、up)事件的层级传递</p></li></ol><h2 id="AsyncTask详解"><a href="#AsyncTask详解" class="headerlink" title="AsyncTask详解"></a>AsyncTask详解</h2><ol><li><p>AsyncTask是一个抽象类，是由Android封装的一个轻量级异步类，它可以在线程池中执行后台任务，然后把执行的进度和最终的结果传递给主线程并在主线程中更新UI<br>AsyncTask的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler).</p></li><li><p>AsyncTask的使用和源码分析见参考资料1</p></li><li><p>AsyncTask使用不当的后果<br>a) 生命周期<br>AsyncTask 不与任何组件绑定生命周期，所以在Activity或者Fragment中创建执行AsyncTask时，最好在Activity/Fragment的onDestory调用cancel方法</p><p>b)内存泄漏<br>如果AsyncTask被声明为Activity的非静态的内部类，那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用.<br>如果Activity已经被销毁，AsyncTask的后台线程还在执行，将继续保留这个引用，导致Activity无法被回收，引起内存泄漏</p><p>c)结果丢失<br>屏幕旋转或者Activity在后台被系统杀掉等情况导致的Activity重建，之前运行的AsyncTask会持有一个之前Activity的引用，这个引用已经无效，这是调用onPOSTExecute()再去更新界面将不再生效.</p><p><a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/asynctask.md" target="_blank" rel="noopener">参考资料1</a></p></li></ol><h2 id="HandlerThread详解"><a href="#HandlerThread详解" class="headerlink" title="HandlerThread详解"></a>HandlerThread详解</h2><p>   在Android中执行耗时的操作都需要另外开启子线程来执行，执行完线程以后会自动销毁，如果项目中经常需要执行耗时操作，如果经常开启线程，接着又销毁线程，无疑会消耗性能，解决方法是：<br>   a)使用线程池<br>   b)使用HandlerThread</p><p>   HandlerThread的代码以及在Framework中封装好：/frameworks/base/core/java/android/os/HandlerThread.java，采用的是Handler和Looper来实现</p><p>   HandlerThread的使用，见参考资料1</p><p>   <a href="https://github.com/LRH1993/android_interview/blob/master/android/basis/HandlerThread.md" target="_blank" rel="noopener">参考资料1</a></p><h2 id="IntentServcie详解"><a href="#IntentServcie详解" class="headerlink" title="IntentServcie详解"></a>IntentServcie详解</h2><p>  IntentService是Android里面的一个封装类，继承自四大组件之一的Service.<br>  作用：处理异步请求，实现多线程.</p><h2 id="LruCache详解"><a href="#LruCache详解" class="headerlink" title="LruCache详解"></a>LruCache详解</h2><p>  Android中缓存策略，一般来说，缓存策略主要包含缓存的添加、获取和删除者三类操作.<br>  不管是内存缓存还是硬盘缓存，它们的缓存大小都是有限的，当缓存满了之后，再向其添加缓存，这时就需要删除一些旧的缓存并添加新的缓存.</p><p>  LRU(Least Recently Used)是最近最少使用算法，采用LRU算法的缓存有两种：LruCache和DisLruCache，分别是内存缓存和硬盘缓存.</p><ol><li><p>LruCache的使用<br>LruCache是Android3.1提供的一个缓存类.</p></li><li><p>LruCache源码<br>看LruCache源码，发现在Android的framework层的代码，大部分实现都用到Java的基础知识.所以还是要好好掌握下Java的基础知识.</p></li></ol><h2 id="Window、Activity、DecorView以及ViewRoot之间的关系"><a href="#Window、Activity、DecorView以及ViewRoot之间的关系" class="headerlink" title="Window、Activity、DecorView以及ViewRoot之间的关系"></a>Window、Activity、DecorView以及ViewRoot之间的关系</h2><h2 id="View的测量、布局以及绘制原理"><a href="#View的测量、布局以及绘制原理" class="headerlink" title="View的测量、布局以及绘制原理"></a>View的测量、布局以及绘制原理</h2><h2 id="Android虚拟机以及编译过程"><a href="#Android虚拟机以及编译过程" class="headerlink" title="Android虚拟机以及编译过程"></a>Android虚拟机以及编译过程</h2><h2 id="Android进程间通信方式"><a href="#Android进程间通信方式" class="headerlink" title="Android进程间通信方式"></a>Android进程间通信方式</h2><h2 id="Android-Bitmap压缩策略"><a href="#Android-Bitmap压缩策略" class="headerlink" title="Android Bitmap压缩策略"></a>Android Bitmap压缩策略</h2><h2 id="Android-动画总结"><a href="#Android-动画总结" class="headerlink" title="Android 动画总结"></a>Android 动画总结</h2><h2 id="Android-进程优先级"><a href="#Android-进程优先级" class="headerlink" title="Android 进程优先级"></a>Android 进程优先级</h2><h2 id="Android-Contex-详解"><a href="#Android-Contex-详解" class="headerlink" title="Android Contex 详解"></a>Android Contex 详解</h2><h1 id="Android进阶"><a href="#Android进阶" class="headerlink" title="Android进阶"></a>Android进阶</h1><h2 id="Android-多线程断点续传"><a href="#Android-多线程断点续传" class="headerlink" title="Android 多线程断点续传"></a>Android 多线程断点续传</h2><h2 id="Android全局异常处理"><a href="#Android全局异常处理" class="headerlink" title="Android全局异常处理"></a>Android全局异常处理</h2><h2 id="Android-MVP模式详解"><a href="#Android-MVP模式详解" class="headerlink" title="Android MVP模式详解"></a>Android MVP模式详解</h2><h2 id="Android-Binder机制以及AIDL使用"><a href="#Android-Binder机制以及AIDL使用" class="headerlink" title="Android Binder机制以及AIDL使用"></a>Android Binder机制以及AIDL使用</h2><h2 id="Android-Parcelable和Serializable的区别"><a href="#Android-Parcelable和Serializable的区别" class="headerlink" title="Android Parcelable和Serializable的区别"></a>Android Parcelable和Serializable的区别</h2><h2 id="一个APP从启动到主页面显示经历了哪些过程"><a href="#一个APP从启动到主页面显示经历了哪些过程" class="headerlink" title="一个APP从启动到主页面显示经历了哪些过程?"></a>一个APP从启动到主页面显示经历了哪些过程?</h2><h2 id="Android-性能优化总结"><a href="#Android-性能优化总结" class="headerlink" title="Android 性能优化总结"></a>Android 性能优化总结</h2><h2 id="Android-内存泄漏总结"><a href="#Android-内存泄漏总结" class="headerlink" title="Android 内存泄漏总结"></a>Android 内存泄漏总结</h2><h2 id="Android-布局优化之include-merge-ViewStub的使用"><a href="#Android-布局优化之include-merge-ViewStub的使用" class="headerlink" title="Android 布局优化之include/merge/ViewStub的使用"></a>Android 布局优化之include/merge/ViewStub的使用</h2><h2 id="Android-权限处理"><a href="#Android-权限处理" class="headerlink" title="Android 权限处理"></a>Android 权限处理</h2><h2 id="Android热修复原理"><a href="#Android热修复原理" class="headerlink" title="Android热修复原理"></a>Android热修复原理</h2><h2 id="Android-插件化入门指南"><a href="#Android-插件化入门指南" class="headerlink" title="Android 插件化入门指南"></a>Android 插件化入门指南</h2><h2 id="VirtualAPK解析"><a href="#VirtualAPK解析" class="headerlink" title="VirtualAPK解析"></a>VirtualAPK解析</h2><h2 id="Android-推送技术解析"><a href="#Android-推送技术解析" class="headerlink" title="Android 推送技术解析"></a>Android 推送技术解析</h2><h2 id="Android-APK安装过程"><a href="#Android-APK安装过程" class="headerlink" title="Android APK安装过程"></a>Android APK安装过程</h2><h2 id="PopupWindow和Dialog区别"><a href="#PopupWindow和Dialog区别" class="headerlink" title="PopupWindow和Dialog区别"></a>PopupWindow和Dialog区别</h2><h1 id="Java-虚拟机"><a href="#Java-虚拟机" class="headerlink" title="Java 虚拟机"></a>Java 虚拟机</h1><p>《深入理解Java虚拟机》–JVM高级特性与最佳实践<br>      周志明-机械工业出版社</p><p>关于Java虚拟机，重点考察以下三个方面：</p><ul><li>内存区域/内存模型</li><li>类加载机制</li><li>垃圾收集算法/收集器</li></ul><p>MyQuestion：</p><ol><li><p>JVM 开源么？ JVM是包含在JDK里面的么？</p></li><li><p>Dalvik对JVM 做了哪些改进？ </p></li><li><p>ART又对Dalvik做了哪些改进？</p></li></ol><p>虚拟机相关的关键字：<br>JVM：<br>HotSpot：<br>JIT<br>AOT:<br>Heap(堆):<br>Stack(栈):</p><h2 id="对象的创建、内存布局和访问定位"><a href="#对象的创建、内存布局和访问定位" class="headerlink" title="对象的创建、内存布局和访问定位"></a>对象的创建、内存布局和访问定位</h2><h3 id="对象的创建"><a href="#对象的创建" class="headerlink" title="对象的创建"></a>对象的创建</h3><ol><li><p>虚拟机遇到一个New指令时，首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用；</p></li><li><p>检查这个符号引用代表的类是否已经被加载，解析和初始化过。如果没有，那必须先执行相应的类的加载过程;</p></li><li><p>在类加载检查功能通过后，为新生对象分配内存。对象所需的内存大小在类加载完成后便可完全确定。</p></li></ol><h3 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h3><p>分为三个区域：  对象头，实例数据，对齐填充。</p><ol><li><p>对象头<br>包含两部分信息，第一部分：对象自身的运行时数据，如哈希吗，GC分代年龄，锁状态标志，线程持有的锁，偏向线程ID，偏向时间戳等。着不放数据的长度在32位和64位虚拟机中分别为32bit和64bit，官方称它为”Mark Word”.</p><p>第二部分：类型指针，即对象指向它的类元数据的指针，虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是一个java数组，那在对象头中还必须有一块用于记录数组长度的数据 。</p></li><li><p>实例数据<br>是对象真正存储的有效信息，也是在程序代码中所定义的各种类型的字段内容。</p></li><li><p>对齐填充<br>对齐填充不是必然存在的。HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍，也就是说对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的整数倍。因此，当对象实例数据部分没有对齐是，就需要通过对齐补充来补全了。</p></li></ol><h3 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h3><p> Java程序需要通过栈上的reference数据来操作堆上的具体对象</p><p> 目前主流的访问方式有使用句柄和直接使用指针两种。</p><ol><li><p>句柄访问<br>Java堆中会划分出一块内存来作为句柄池，reference中存储的事就对象的句柄地址，而句柄中包含了对实例数据与类型数据的各自具体的地址信息。<br><img src="/Android-InterView/" alt="句柄访问"></p></li><li><p>直接指针访问<br>reference中存储的直接就是对象地址<br><img src="/Android-InterView/" alt="直接指针访问"></p></li></ol><h2 id="Java内存区域"><a href="#Java内存区域" class="headerlink" title="Java内存区域"></a>Java内存区域</h2><ol><li><p>方法区(公有)</p></li><li><p>堆(公有)</p></li><li><p>虚拟机栈(线程私有)</p></li><li><p>本地方法栈(线程私有)</p></li><li><p>程序计数器(线程私有)</p></li></ol><h2 id="Java内存模型"><a href="#Java内存模型" class="headerlink" title="Java内存模型"></a>Java内存模型</h2><p> Java内存模型的目的： 屏蔽掉各种硬件和操作系统的内存访问差异，以实现让java程序在各种平台下都能达到一致的内存访问效果。</p><p> 主要目标：定义程序中各个变量的访问规则，即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。</p><p> 了解Java内存模型之前需要了解：主内存和工作内存。</p><p> Java内存模型规定了所有的变量都存储在主内存中，每个线程中还有自己的工作内存，线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取、赋值)都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量，线程间变量值的传递均需要通过主内存来完成。</p><h2 id="Java类加载机制"><a href="#Java类加载机制" class="headerlink" title="Java类加载机制"></a>Java类加载机制</h2><ol><li><p>定义： 把描述类的数据从class文件加载到内存，并对数据进行校验、转换解析和初始化，最终形成可以被虚拟机直接使用的Java类型。</p><p>在Java语言里，类型的加载、连接和初始化过程都是在程序运行期间完成的，这种策略虽然会令类加载时稍微增加一些性能开销，但是会为Java应用程序提供高度的灵活性，Java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态链接这个特点来实现的。</p></li><li><p>类的生命周期：  加载、验证、准备、解析、初始化、使用和卸载。其中验证，准备，解析3个部分统称为链接。<br>Loading -&gt; Verification -&gt; Preparation -&gt; Resolution -&gt; Initalization -&gt; Using -&gt; Unloading</p></li></ol><h2 id="类加载器详解"><a href="#类加载器详解" class="headerlink" title="类加载器详解"></a>类加载器详解</h2><p> 通过上述的了解，我们已经知道了类加载机制的大概流程以及各个部分的功能，其中加载部分的功能是将类的class文件读入内存，并为之创建一个java.lang.Class对象。这部分功能就是由类加载器来实现的。</p><ol><li><p>类加载器分类<br>不同的类加载器负责加载不同的类。主要分为两类：<br>启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对Hotspot),负责将存放在&lt;JAVA_HOME&gt;/lib目录或者-Xbootclasspath参数指定的路径中的类库加载到内存中，即负责加载Java的核心类</p><p>其他类加载器：由Java语言实现，继承自抽象类ClassLoader。如：</p><p>扩展类加载器(Extension ClassLoader)：负责将存放在&lt;JAVA_HOME&gt;/lib/ext目录或者java.ext.dirs系统变量指定的路径中的类库加载到内存中，即负责加载Java扩展的核心类之外的类</p><p>应用程序类加载器(Application ClassLoader): </p></li><li><p>双亲委派模型<br>双亲委派模型的工作流程是：如果一个类加载器收到了类加载的请求，它首先不会自己去尝试加载这个类，而是把请求委托给父加载器去完成，依次向上，因此，所有的类加载请求最终都应该被传递到顶层的启动类加载器中，只有父加载器在它的搜索范围内没有找到所需的类时，即无法完成该加载，子加载才会尝试自己去加载该类。</p></li></ol><ol start="3"><li>双亲委派模型的代码实现<br>ClassLoader中loadClass方法实现了双亲委派模型</li></ol><h2 id="JVM中垃圾收集算法"><a href="#JVM中垃圾收集算法" class="headerlink" title="JVM中垃圾收集算法"></a>JVM中垃圾收集算法</h2><ol><li><p>标记-清除算法<br>最基础的收集算法是”标记-清除”(Mark-Sweep)算法，分为标记和清除两个阶段<br>a)首先标记出所需要回收的对象<br>b)在标记完成之后，统一回收所有被标记的对象</p><p>不足：<br>效率问题： 标记和清除两个过程的效率都不高<br>空间问题：标记清除后产生大量的不连续的内存碎片，空间碎片太多，可能会导致分配大对象时，无法找到足够连续的内存而不得不提前触发另一次垃圾收集动作</p></li><li><p>复制算法<br>目的：为了解决效率问题</p><p>将可用内存按照容量大小划分为大小相等的两块，每次只使用其中的一块。当一块内存使用完了，就将还存活的对象复制到另一块上面，然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收，内存分配时也就不用考虑内存碎片等复杂情况。</p><p>缺点：将内存缩小为了原来的一半</p><p>现代的商业虚拟机都是采用这种收集算法来回收新生代，IBM公司的专门研究表明，新生代中对象98%对象是”朝生夕死”，所以不需要按照1:1来划分内存空间，而是将内存分为较大的Eden空间和两块较小的Survivor空间，每次使用Eden和其中一块Survivor。HotSpot虚拟机中默认Eden和Survivor的大小比例是8:1</p></li><li><p>标记-整理算法<br>复制算法在对象存活率较高的时候，就要进行较多的复制操作，效率就会变低。</p><p>标记过程和标记清除算法一样，但后续步骤不是直接对可回收对象进行清理，而是让所有存活对象都向一端移动，然后直接清理掉边界以外的内存。</p></li><li><p>分代收集算法<br>一般把Java堆分为新生代和老年代，这样就可以根据各个年代的特点采用最适当的收集算法。</p><p>对新生代，采用复制算法</p><p>对老年代，采用标记整理算法或者标记清除算法</p></li></ol><p>##垃圾收集器详解</p><h2 id="JVM怎么判断对象是否已死"><a href="#JVM怎么判断对象是否已死" class="headerlink" title="JVM怎么判断对象是否已死"></a>JVM怎么判断对象是否已死</h2><ol><li><p>引用计数法<br>给对象添加一个引用计数器，每当有一个地方引用它时，计数器值就加1；当引用失效时，计数器值就减1.任何时刻计数器为0的对象就是不可能被在使用的。</p><p>主流的JVM里面没有选用引用计数器算法来管理内存，其中最主要的原因是它很难解决对象间的互相循环引用问题。</p></li><li><p>可达性分析算法</p></li><li><p>判断对象是否存活与”引用”相关<br>Strong Reference 强引用<br>Soft Reference 软引用<br>Weak Reference 弱引用<br>Phantom Reference 虚引用<br>引用强度依次减弱</p></li></ol><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://github.com/LRH1993/android_interview" target="_blank" rel="noopener">Android校招面试指南</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;!--
# 自己简历上写的技能：
1.对Android非常感兴趣，对移动互联网充满激情，愿意未来二十年从事移动互联网行业;
2.Android SDK以及Android各组件应用、JNI开发、多线程、线程池的使用;
3.熟悉Android的SystemUI/Keyguar
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="面试" scheme="http://yoursite.com/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>Android中Launcher解析</title>
    <link href="http://yoursite.com/Android-Launcher-Analysis/"/>
    <id>http://yoursite.com/Android-Launcher-Analysis/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-09-06T03:17:27.723Z</updated>
    
    <content type="html"><![CDATA[<p>Ongoing,待完成，Please wait.</p><h1 id="Launcher-概述"><a href="#Launcher-概述" class="headerlink" title="Launcher 概述"></a>Launcher 概述</h1><p>Launcher3代码统计:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">Language                     files          blank        comment           code</span><br><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">Java                           354          10876          13510          57150</span><br><span class="line">XML                            309           1074           4971          13498</span><br><span class="line">Python                           2             49             31            323</span><br><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">SUM:                           665          11999          18512          70971</span><br><span class="line">-------------------------------------------------------------------------------</span><br></pre></td></tr></table></figure></p><p>开始学习Launcher3源码之前请务必保证你已经具备如下图所示基础知识技能（相关权重系数已经饼状图标注），如下：<br><img src="/Android-Launcher-Analysis/Launcher-01.jpg" alt="&quot;Launcher需要知识图谱&quot;"><br>可以形象的认为整个原生Launcher就是以MVC为宏观指导、以View为模块的结构；全局有效的核心Activity只有一个，但是其中涉及了多种交互状态，每种状态都至少依赖于一到多个庞大而复杂的自定义View，每一个事件的处理都需要考虑不同状态下不同层级、不同触摸位置的派发拦截处理流程。</p><p>其实Launcher的实质也就是一个普通应用，它只是比普通应用多配置了Category<br>的android:name=”android.intent.category.HOME”属性而已。当Android开机启动成功以后框架层会尝试启动包含上面属性配置的Activity，这样被启动的那个Activity就成了桌面。当我们按下设备的Home键时也会触发包含该属性的Activity。只不过当系统中只存在一个包含该属性的应用时，无论开机还是Home键触发都只会自动启动默认的；当存在多个时无论哪种触发都会弹出选择框进行选择设置。</p><p>在手机设备上我们为了保留尽可能的兼容性而没有干掉该选框，在机顶盒开发中大多数厂商为了导流和推自己的视频服务一般都会修改框架层的ActivityManagerService服务将该入口写死.</p><p>Android系统开机启动Launcher的大致过程，如下：<br><img src="/Android-Launcher-Analysis/Launcher-02.jpg" alt="&quot;Launcher启动流程&quot;"></p><p>Launcher界面的划分，如下是Launcher结构的模块划分：<br><img src="/Android-Launcher-Analysis/Launcher-03.jpg" alt="&quot;Launcher布局结构&quot;"></p><p>如下是原生Launcher3经典的四种UI模式：<br><img src="/Android-Launcher-Analysis/Launcher-04.jpg" alt="&quot;Launcher布局结构&quot;"></p><p>通过上面这两幅图可以知道，Launcher3的实质其实就是一个Activity包含N个自定义层级的View，不同模式隐藏显示不同的View而已。<br>参考<a href="https://blog.csdn.net/yanbober/article/details/50525559" target="_blank" rel="noopener">Android M Launcher3主流程源码浅析</a></p><h1 id="主要布局文件解析"><a href="#主要布局文件解析" class="headerlink" title="主要布局文件解析"></a>主要布局文件解析</h1><p> 下面将对一些主要的布局文件进行解析.</p><h2 id="launcher-xml"><a href="#launcher-xml" class="headerlink" title="launcher.xml"></a>launcher.xml</h2><h1 id="主要类解析"><a href="#主要类解析" class="headerlink" title="主要类解析"></a>主要类解析</h1><p> 下面将对上面提到的一些主要的类进行解析.</p><h2 id="Launcher-java"><a href="#Launcher-java" class="headerlink" title="Launcher.java"></a>Launcher.java</h2><p>主界面Activity，最核心且唯一的Activity。Launcher.java继承子BaseActivity.java，实现了一些接口，具体如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Launcher</span> <span class="keyword">extends</span> <span class="title">BaseActivity</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">LauncherExterns</span>, <span class="title">View</span>.<span class="title">OnClickListener</span>, <span class="title">OnLongClickListener</span>,</span></span><br><span class="line"><span class="class">                   <span class="title">LauncherModel</span>.<span class="title">Callbacks</span>, <span class="title">View</span>.<span class="title">OnTouchListener</span>, <span class="title">LauncherProviderChangeListener</span>,</span></span><br><span class="line"><span class="class">                   <span class="title">AccessibilityManager</span>.<span class="title">AccessibilityStateChangeListener</span>,</span></span><br><span class="line"><span class="class">                   <span class="title">WallpaperColorInfo</span>.<span class="title">OnThemeChangeListener</span> </span>&#123;</span><br><span class="line">                   ....</span><br><span class="line">                   &#125;</span><br></pre></td></tr></table></figure></p><p>而BaseActivity.java继承子Activity， 包含DeviceProfile.java，UserEventDispatcher.java,SystemUiController.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> DeviceProfile mDeviceProfile;</span><br><span class="line">    <span class="keyword">protected</span> UserEventDispatcher mUserEventDispatcher;</span><br><span class="line">    <span class="keyword">protected</span> SystemUiController mSystemUiController;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>LauncherExterns.java接口，暴露方法给其他应用调用，主要用于测试<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">LauncherExterns</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">setLauncherCallbacks</span><span class="params">(LauncherCallbacks callbacks)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> SharedPreferences <span class="title">getSharedPrefs</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLauncherOverlay</span><span class="params">(Launcher.LauncherOverlay overlay)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">clearTypedText</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="DeviceProfile-java"><a href="#DeviceProfile-java" class="headerlink" title="DeviceProfile.java"></a>DeviceProfile.java</h2><p>定义了Launcher中各个模块的一些基本属性.以及属性值的初始化等工作.<br>设置各元素布局的padding  。修改workspace的padding使用getWorkspacePadding方法。</p><h2 id="InvariantDeviceProfile-java"><a href="#InvariantDeviceProfile-java" class="headerlink" title="InvariantDeviceProfile.java"></a>InvariantDeviceProfile.java</h2><p>一些不变的设备相关参数管理类，其内部包涵了横竖屏模式的DeviceProfile。<br>getPredefinedDeviceProfiles方法 负责加载在不同设备上不同的布局，和图标大小等。</p><h2 id="SystemUiController-java"><a href="#SystemUiController-java" class="headerlink" title="SystemUiController.java"></a>SystemUiController.java</h2><p>用于管理Launcher界面时，SystemUI中的StatusBar和NavigationBar的颜色</p><h2 id="LauncherAppState-java"><a href="#LauncherAppState-java" class="headerlink" title="LauncherAppState.java"></a>LauncherAppState.java</h2><p>单例模式的全局管理类,主要是初始化一些对象,注册广播等.单例对象，构造方法中初始化对象、注册应用安装、卸载、更新，配置变化等广播。这些广播用来实时更新桌面图标等，其receiver的实现在LauncherModel类中，LauncherModel也在这里初始化。<br>控制和管理一下Launcher模块的大类的初始化和管理.<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Context mContext;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> LauncherModel mModel;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> IconCache mIconCache;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> WidgetPreviewLoader mWidgetCache;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> InvariantDeviceProfile mInvariantDeviceProfile;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> SettingsObserver mNotificationBadgingObserver;</span><br></pre></td></tr></table></figure></p><h2 id="LauncherModel-java"><a href="#LauncherModel-java" class="headerlink" title="LauncherModel.java"></a>LauncherModel.java</h2><p>继承BroadcastReceiver,由此可知他是一个广播接收器,用来接收广播,另外,LauncherModel还主要加载数据;<br>数据处理类，保存桌面状态，提供读写数据库的API，内部类LoaderTask用来初始化桌面。</p><h2 id="InstallShortcutReceiver-java"><a href="#InstallShortcutReceiver-java" class="headerlink" title="InstallShortcutReceiver.java"></a>InstallShortcutReceiver.java</h2><p>继承自BroadcastReceiver.</p><h2 id="LauncherAppsCompat-java"><a href="#LauncherAppsCompat-java" class="headerlink" title="LauncherAppsCompat.java"></a>LauncherAppsCompat.java</h2><p>获取已安装App列表信息的兼容抽象基类，子类依据不同版本API进行兼容性处理。<br>子类有LauncherAppsCompatVL.java和LauncherAppsCompatVO.java，其中LauncherAppsCompatVO处理O版本以及以上的版本.</p><h2 id="WidgetPreviewLoader-java"><a href="#WidgetPreviewLoader-java" class="headerlink" title="WidgetPreviewLoader.java"></a>WidgetPreviewLoader.java</h2><p>存储Widget信息的数据库，内部创建了数据库widgetpreviews.db.</p><h2 id="AppWidgetManagerCompat-java"><a href="#AppWidgetManagerCompat-java" class="headerlink" title="AppWidgetManagerCompat.java"></a>AppWidgetManagerCompat.java</h2><p>获取AppWidget列表的兼容抽象基类，子类依据不同版本API进行兼容性处理。</p><h2 id="LauncherStateTransitionAnimation-java"><a href="#LauncherStateTransitionAnimation-java" class="headerlink" title="LauncherStateTransitionAnimation.java"></a>LauncherStateTransitionAnimation.java</h2><p>各类动画总管处理执行类，负责各种情况下的各种动画效果处理</p><h2 id="DragLayer-java"><a href="#DragLayer-java" class="headerlink" title="DragLayer.java"></a>DragLayer.java</h2><p>一个用来负责分发事件的ViewGroup</p><h2 id="DragController-java"><a href="#DragController-java" class="headerlink" title="DragController.java"></a>DragController.java</h2><p>DragLayer只是一个ViewGroup，具体的拖拽的处理都放到了DragController中。</p><h2 id="LauncherProvider-java"><a href="#LauncherProvider-java" class="headerlink" title="LauncherProvider.java"></a>LauncherProvider.java</h2><p>继承ContentProvider,主要是处理数据库操作;<br>核心数据库类，负责launcher.db的创建与维护。</p><h2 id="LauncherAppWidgetHost-java"><a href="#LauncherAppWidgetHost-java" class="headerlink" title="LauncherAppWidgetHost.java"></a>LauncherAppWidgetHost.java</h2><p>AppWidgetHost子类，是桌面插件宿主，为了方便托拽等才继承处理的。</p><h2 id="LauncherAppWidgetHostView-java"><a href="#LauncherAppWidgetHostView-java" class="headerlink" title="LauncherAppWidgetHostView.java"></a>LauncherAppWidgetHostView.java</h2><p>AppWidgetHostView子类，配合LauncherAppWidgetHost得到HostView。</p><h2 id="LauncherRootView-java"><a href="#LauncherRootView-java" class="headerlink" title="LauncherRootView.java"></a>LauncherRootView.java</h2><p>竖屏模式下根布局，继承了InsettableFrameLayout，控制是否显示在状态栏等下面。</p><h2 id="BubbleTextView-java"><a href="#BubbleTextView-java" class="headerlink" title="BubbleTextView.java"></a>BubbleTextView.java</h2><p>图标都基于他，继承自TextView。</p><h2 id="DragView-java"><a href="#DragView-java" class="headerlink" title="DragView.java"></a>DragView.java</h2><p>拖动图标时跟随手指移动的View。</p><h2 id="Floder-java"><a href="#Floder-java" class="headerlink" title="Floder.java"></a>Floder.java</h2><p>打开文件夹展示的View。</p><h2 id="FolderIcon-java"><a href="#FolderIcon-java" class="headerlink" title="FolderIcon.java"></a>FolderIcon.java</h2><p>文件夹图标.</p><h2 id="DragSource-java-DropTarget-java"><a href="#DragSource-java-DropTarget-java" class="headerlink" title="DragSource.java/DropTarget.java"></a>DragSource.java/DropTarget.java</h2><p>拖拽接口，DragSource表示图标从哪开始拖，DropTarget表示图标被拖到哪去。</p><h2 id="Hotseat-java"><a href="#Hotseat-java" class="headerlink" title="Hotseat.java"></a>Hotseat.java</h2><p>用来放置比较常用的应用，比如拨号，短信，相机等。</p><h2 id="WorkSpace-java"><a href="#WorkSpace-java" class="headerlink" title="WorkSpace.java"></a>WorkSpace.java</h2><p>就是屏幕上左右滑的好几屏幕的容器</p><h2 id="PageView-java"><a href="#PageView-java" class="headerlink" title="PageView.java"></a>PageView.java</h2><h2 id="CellLayout-java"><a href="#CellLayout-java" class="headerlink" title="CellLayout.java"></a>CellLayout.java</h2><p>继承自ViewGroup，实现BubbleTextShadowHandler接口,<br>Workspace里面可以滑动的单独一屏，CellLayout负责图标和小部件的显示和整齐摆放。<br>mCountX 和 mCountY   分别表示 “x方向icon的个数” 和 “y方向icon的个数”<br>mOccupied 二维数组用来标记每个cell是否被占用，内部类CellInfo为静态类，其对象用于存储cell的基本信息。 </p><h2 id="GridOccupancy-java"><a href="#GridOccupancy-java" class="headerlink" title="GridOccupancy.java"></a>GridOccupancy.java</h2><p>用于管理每个网格的占用情况</p><h2 id="PageIndicator-java"><a href="#PageIndicator-java" class="headerlink" title="PageIndicator.java"></a>PageIndicator.java</h2><p>滑动屏幕的时候看见下方的指示器</p><h2 id="BaseRecyclerView-java"><a href="#BaseRecyclerView-java" class="headerlink" title="BaseRecyclerView.java"></a>BaseRecyclerView.java</h2><p>BaseRecyclerView继承自RecyclerView，实现了RecyclerView.OnItemTouchListener.包含一个RecyclerViewFastScroller类型的变量.<br>处理workspace界面的左右滑动事件</p><h2 id="AllAppsRecyclerView-java"><a href="#AllAppsRecyclerView-java" class="headerlink" title="AllAppsRecyclerView.java"></a>AllAppsRecyclerView.java</h2><p>继承自BaseRecyclerView，实现了LogContainerProvider接口.<br>A RecyclerView with custom fast scroll support for the all apps view.</p><h2 id="BaseContainerView-java"><a href="#BaseContainerView-java" class="headerlink" title="BaseContainerView.java"></a>BaseContainerView.java</h2><p>抽象类</p><h2 id="AllAppsContainerView-java"><a href="#AllAppsContainerView-java" class="headerlink" title="AllAppsContainerView.java"></a>AllAppsContainerView.java</h2><p>继承BaseContainerView，实现DragSource，View.OnLongClickListener, Insettable接口.<br>The all apps view container.</p><h2 id="AllAppsList-java"><a href="#AllAppsList-java" class="headerlink" title="AllAppsList.java"></a>AllAppsList.java</h2><p>存储所有应用程序列表<br>包含两个属性:</p><blockquote><p>IconCache<br>AppFilter APP过滤，后面可以扩充AppFilter类，实现需要的过滤功能.</p></blockquote><h2 id="IconCache-java"><a href="#IconCache-java" class="headerlink" title="IconCache.java"></a>IconCache.java</h2><p>图标缓存类，应用程序icon和title的缓存，内部类创建了数据库app_icons.db。<br>一个CacheEntry包含如下四个属性:</p><blockquote><p>Bitmap icon 图标图片<br>title 图标标题<br>contentDescription 图标描述<br>isLowResIcon</p></blockquote><h2 id="AppInfo-java"><a href="#AppInfo-java" class="headerlink" title="AppInfo.java"></a>AppInfo.java</h2><p>Represents an app in AllAppsView.在AllApp界面呈现一个应用的APP信息.继承自ItemInfoWithIcon。<br>新增有四个属性：</p><blockquote><p>Intent  用于启动某个APP的Intent<br>ComponentName 该App的组件名<br>isDisabled<br>isSystemApp  该APP是否为系统APP</p></blockquote><h2 id="LauncherActivityInfo-java"><a href="#LauncherActivityInfo-java" class="headerlink" title="LauncherActivityInfo.java"></a>LauncherActivityInfo.java</h2><p>LauncherActivityInfo.java代码不在Launcher模块，在framework模块,但是在Launcher模块用的比较多，所以也放在Launcher模块一起分析.<br>代码路径：framework/base/core/java/android/content/pm/LauncherActivityInfo.java<br>A representation of an activity that can belong to this user or a managed profile associated with this user. It can be used to query the label, icon and badged icon for the activity.<br>包含三个属性：</p><blockquote><p>ActivityInfo<br>ComponentName 组件信息<br>UserHandle 用户信息</p></blockquote><h2 id="ShortcutCache-java"><a href="#ShortcutCache-java" class="headerlink" title="ShortcutCache.java"></a>ShortcutCache.java</h2><p>快捷图标缓存</p><h2 id="ShortcutKey-java"><a href="#ShortcutKey-java" class="headerlink" title="ShortcutKey.java"></a>ShortcutKey.java</h2><p>ShortcutKey继承自ComponentKey.通过packageName，ShortcutInfoCompat，intent，ItemInfo等方式得到快捷方式的唯一Key.</p><h2 id="ComponentKey-java"><a href="#ComponentKey-java" class="headerlink" title="ComponentKey.java"></a>ComponentKey.java</h2><p>ComponentKey包含两个属性componentName和UserHandle，实际上是将这两个属性进行了绑定，进行了一一映射.同时为了防止篡改，还对每一对分配了一个hasCode值.<br>同时使用Preconditions类保证线程安全.</p><h2 id="ShortcutInfo-java"><a href="#ShortcutInfo-java" class="headerlink" title="ShortcutInfo.java"></a>ShortcutInfo.java</h2><p>Represents a launchable icon on the workspaces and in folders.<br>在桌面或者文件夹里面显示的快捷方式图标.继承自ItemInfoWithIcon.<br>有一些标志位：</p><blockquote><p>FLAG_RESTORED_ICON：此图标来自于备份，暂时还不能使用.<br>FLAG_AUTOINSTALL_ICON: 由自动安装的APP添加，暂时还不能使用.<br>FLAG_INSTALL_SESSION_ACTIVE:<br>FLAG_RESTORE_STARTED:<br>FLAG_SUPPORTS_WEB_UI:<br>FLAG_RESTORED_APP_TYPE:<br>FLAG_DISABLED_SAFEMODE:处于安全模式，该图标暂时不能使用.<br>FLAG_DISABLED_NOT_AVAILABLE:由于该图标的APP不能使用，使得该图标不能使用.<br>FLAG_DISABLED_SUSPENDED:由于该图标的APP被suspended(暂停)，使得该图标不能使用.<br>FLAG_DISABLED_QUIET_USER:用户处于quiet mode(静音模式),使得该图标不能使用.<br>FLAG_DISABLED_BY_PUBLISHER:应用发布者禁用快捷方式图标，使得该图标不能使用.<br>FLAG_DISABLED_LOCKED_USER:当前用户被锁单，使得该图标不能使用.<br>新增以下属性：<br>isDisabled 某些情况下APP已经安装，但是快捷图标不能使用，比如：进入安全模式或者SD卡不能使用的情况下<br>disabledMessage 当用户尝试点击一个不能使用的图标时显示不能使用的信息.主要用于深度快捷图标.<br>mInstallProgress 在快捷方式中显示APP的安装进度.以百分比显示.<br>intent 启动APP的Intent<br>Intent.ShortcutIconResource<br>status 状态，由上面列出来的标志位来标识.</p></blockquote><h2 id="ItemInfoWithIcon-java"><a href="#ItemInfoWithIcon-java" class="headerlink" title="ItemInfoWithIcon.java"></a>ItemInfoWithIcon.java</h2><p>抽象类，继承自ItemInfo.java,Represents an ItemInfo which also holds an icon.<br>相对于ItemInfo增加了两个属性:</p><blockquote><p>iconBitmap  储存应用的图标<br>usingLowResIcon 标明是否使用低资源资源图标.啥叫低资源图标，没搞懂</p></blockquote><h2 id="ItemInfo-java"><a href="#ItemInfo-java" class="headerlink" title="ItemInfo.java"></a>ItemInfo.java</h2><p>Represents an item in the launcher.<br>包含如下属性：</p><blockquote><p>itemType,itemType的类型有：ITEM_TYPE_APPLICATION，ITEM_TYPE_SHORTCUT，ITEM_TYPE_DEEP_SHORTCUT，ITEM_TYPE_FOLDER，ITEM_TYPE_APPWIDGET，ITEM_TYPE_CUSTOM_APPWIDGET.<br>container的种类有：CONTAINER_DESKTOP，CONTAINER_HOTSEAT<br>screenId 表明处于第几屏<br>cellX:表明第几行<br>cellY:表明第几列<br>spanX:宽度<br>spanY:高度<br>rank: 标明在一个Item列表中的位置<br>title: Item的名称<br>contentDescription: Item的描述<br>user: 用户</p></blockquote><p>桌面上每个Item的信息数据结构，包括在第几屏、第几行、第几列、宽高等信息；该对象与数据库中记录一一对应；该类有多个子类，譬如FolderIcon的FolderInfo、BubbleTextView的ShortcutInfo等。<br>参见<a href="https://blog.csdn.net/xufeifandj/article/details/39851741" target="_blank" rel="noopener">Launcher 细说ItemInfo</a></p><p>要理解ItemInfo，就要先理解桌面有哪几种东西，即有哪几种Item。</p><blockquote><p>小工具：就是AppWidget，时钟小工具，天气小工具，等等。<br>快捷方式：快速启动应用的图标，一个应用可以有多个重复的快捷方式，workspace和hotseat上的都是快捷方式<br>文件夹：可以把多个快捷方式放到一块的，用来分类，减少屏幕空间占用。<br>这些东西都一些共同的，可以抽象出来的东西，有自己的宽度和高度，都有所在的位置和第几等等，而ItemInfo就是抽象出来的东西，打开ItemInfo.java,其中包含的就是这些特征，然而，最关键的是ItemInfo对象和数据库记录是一一对应的。其中有个id的成员，代表的就是数据库中ID。</p></blockquote><p>对于不同的item，ItemInfo也有不同的子类，小工具对应的是LauncherAppWidgetInfo，他增加了小工具的信息（查android文档:AppWidgetProviderInfo);快捷方式对应的是ShortcutInfo，他增加了启动一个Activity所需的Intent信息；文件夹对应的是FolderInfo，他增加了文件夹是否打开的标签，文件夹内图标的信息等等。</p><p>ItemInfo的成员有几个值得说说：</p><blockquote><p>container：表明图标是放在哪里的，是放在Workspace还是Hotseat，还是文件夹里面的。如果是放在Workspace上的，那么值是LauncherSettings.Favorites.CONTAINER_DESKTOP,如果是放在文件夹里面的那么container的值就是文件夹FolderInfo的id。<br>cellX，cellY：表明所在屏幕的哪个位置，cellY表明第几行，cellX表明第几列。如果是小工具占用多行多列的情况，就记录他左上角的位置。<br>spanX,spanY:宽度和高度，快捷方式和文件夹宽高都是1，小工具的宽高就要看具体情况了。<br>title：标题，显示应用的名字，文件夹的名字，小工具的话就不需要这个属性了。<br>itemType: 数据库里保存的表明这个ItemInfo具体是哪种类型的ItemInfo，启动的时候好生成具体的ItemInfo子类对象。<br>ItemInfo就这么多了，如果你自己看类的继承关系的话，就知道ItemInfo还有几个子类，比如AppInfo，PendingAddItemInfo，我暂时还没有搞清楚这些类的使用流程，等以后搞清楚了再加上来吧。</p></blockquote><p>再说几个关于数据库的类，方便以后找代码：</p><blockquote><p>LauncherProvider: 桌面信息的ContentProvider。<br>LauncherSettings:存了数据库相关的常量，字段名，字段常量等等。<br>DatabaseHelper: LaucherProvider的内部类，继承自SQLiteOpenHelper,数据库表的创建就是在它的onCreate方法里完成的。</p></blockquote><h1 id="WorkSpace左右滑动处理分析"><a href="#WorkSpace左右滑动处理分析" class="headerlink" title="WorkSpace左右滑动处理分析"></a>WorkSpace左右滑动处理分析</h1><p>参考<a href="https://blog.csdn.net/u011694328/article/details/51514076" target="_blank" rel="noopener">Android Launcher浅析(一)</a></p><h1 id="Launcher图标-Widget-文件夹等加载流程"><a href="#Launcher图标-Widget-文件夹等加载流程" class="headerlink" title="Launcher图标,Widget,文件夹等加载流程"></a>Launcher图标,Widget,文件夹等加载流程</h1><p>Launcher加载图标分两种情况，<br>情况一：带AllApp功能的，在Workspace里面的是Shorcut快捷方式的加载<br>情况二：不带AllApp功能的，在Workspace里面的就是APP的ICON的方式</p><p>第一步在Launcher.java的onCreate()方法中主要完成以下几件事:<br>a) 壁纸颜色管理初始化，以及主题的监听<br>b) LauncherAppState实例化，以及通过LauncherAppState来管理的LauncherModel、IconCache等的初始化<br>c) View的加载和初始化<br>d) Widget和Drag的初始化<br>e) 旋转的处理<br>f) mModel.startLoader(currentScreen)</p><p>对于图标的加载，我们从LauncherModel.startLoader函数开始追踪.</p><p>LauncherModel<br>LoaderTask</p><h1 id="Launcher图标加边框"><a href="#Launcher图标加边框" class="headerlink" title="Launcher图标加边框"></a>Launcher图标加边框</h1><p>在O版本的Launcher中，长按桌面，进入设置，有一个隐藏的开关.<br>打开此开关的方式：到设置里面开启开发者选项,然后重启手机，接着进入Launcher的设置界面.会出现”更改图标形状”的选项.<br>点击后，有默认，方形，方圆形，圆形和泪珠形可以选择.</p><h2 id="更改图标形状-设置界面"><a href="#更改图标形状-设置界面" class="headerlink" title="更改图标形状 设置界面"></a>更改图标形状 设置界面</h2><p>SettingsActivity.java 里面onCreate方法中,来加载设置选项<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);</span><br><span class="line"><span class="keyword">if</span> (iconShapeOverride != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (IconShapeOverride.isSupported(getActivity())) &#123;</span><br><span class="line">                    IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    getPreferenceScreen().removePreference(iconShapeOverride);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br></pre></td></tr></table></figure></p><p>对应的布局文件在launcher_preferences.xml中<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">PreferenceScreen</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>&gt;</span></span><br><span class="line">......</span><br><span class="line">    <span class="tag">&lt;<span class="name">ListPreference</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:key</span>=<span class="string">"pref_override_icon_shape"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:title</span>=<span class="string">"@string/icon_shape_override_label"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:summary</span>=<span class="string">"%s"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:entries</span>=<span class="string">"@array/icon_shape_override_paths_names"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:entryValues</span>=<span class="string">"@array/icon_shape_override_paths_values"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:defaultValue</span>=<span class="string">""</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:persistent</span>=<span class="string">"false"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">PreferenceScreen</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>在这个xml文件中，重点关注entryValues的值，如下：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Values for icon shape overrides. These should correspond to entries defined</span></span><br><span class="line"><span class="comment"> in icon_shape_override_paths_names --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">string-array</span> <span class="attr">translatable</span>=<span class="string">"false"</span> <span class="attr">name</span>=<span class="string">"icon_shape_override_paths_values"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span><span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>M50,0L100,0 100,100 0,100 0,0z<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">string-array</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">string-array</span> <span class="attr">translatable</span>=<span class="string">"false"</span> <span class="attr">name</span>=<span class="string">"icon_shape_override_paths_names"</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Option to not change the icon shape on home screen. [CHAR LIMIT=50] --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>@string/icon_shape_system_default<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>@string/icon_shape_square<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>@string/icon_shape_squircle<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>@string/icon_shape_circle<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">item</span>&gt;</span>@string/icon_shape_teardrop<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">string-array</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>可以看到icon_shape_override_paths_values对应的是.9图的路径,通过工具可以看到上面的四种路径分别对应了图标的4中形状.</p><h2 id="如何实现更改Launcher图标边框"><a href="#如何实现更改Launcher图标边框" class="headerlink" title="如何实现更改Launcher图标边框"></a>如何实现更改Launcher图标边框</h2><p>从上面的布局文件可以看到.设置等处理主要是在IconShapeOverride.java中实现的.<br>获取当前图标形状主要是通过getAppliedValue函数获取，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">getAppliedValue</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> getDevicePrefs(context).getString(KEY_PREFERENCE, <span class="string">""</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在设置界面设置完之后，应用生效处理在apply()函数中：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">apply</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!Utilities.ATLEAST_OREO) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    String path = getAppliedValue(context);</span><br><span class="line">    <span class="keyword">if</span> (TextUtils.isEmpty(path)) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!isSupported(context)) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// magic</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Resources override =</span><br><span class="line">                <span class="keyword">new</span> ResourcesOverride(Resources.getSystem(), getConfigResId(), path);</span><br><span class="line">        getSystemResField().set(<span class="keyword">null</span>, override);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        Log.e(TAG, <span class="string">"Unable to override icon shape"</span>, e);</span><br><span class="line">        <span class="comment">// revert value.</span></span><br><span class="line">        getDevicePrefs(context).edit().remove(KEY_PREFERENCE).apply();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>通过查找代码，发现IconShapeOverride.apply函数在LauncherProvider.java的onCreate函数中有调用.<br>在IconShapeOverride里面，我们可以看到在点击完切换图标样式之后，Launcher进程会自杀.自杀完之后会重新启动.</p><p>在apply函数中，最重要的两句代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Resources override =</span><br><span class="line">                    <span class="keyword">new</span> ResourcesOverride(Resources.getSystem(), getConfigResId(), path);</span><br><span class="line">            getSystemResField().set(<span class="keyword">null</span>, override);</span><br></pre></td></tr></table></figure></p><p>其中ResourcesOverride代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ResourcesOverride</span> <span class="keyword">extends</span> <span class="title">Resources</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> mOverrideId;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String mOverrideValue;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"deprecation"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ResourcesOverride</span><span class="params">(Resources parent, <span class="keyword">int</span> overrideId, String overrideValue)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(parent.getAssets(), parent.getDisplayMetrics(), parent.getConfiguration());</span><br><span class="line">        mOverrideId = overrideId;</span><br><span class="line">        mOverrideValue = overrideValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@NonNull</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getString</span><span class="params">(<span class="keyword">int</span> id)</span> <span class="keyword">throws</span> NotFoundException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (id == mOverrideId) &#123;</span><br><span class="line">            <span class="keyword">return</span> mOverrideValue;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.getString(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>getSystemResField函数的实现如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Field <span class="title">getSystemResField</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    Field staticField = Resources.class.getDeclaredField(<span class="string">"mSystem"</span>);</span><br><span class="line">    staticField.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">    <span class="keyword">return</span> staticField;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以看到getSystemResField函数返回值是Field，也就是说需要通过反射来改变某个属性的值.<br>从代码流程上来看，是通过反射机制，将设置中的.9路径值，重新赋值给config_icon_mask这个属性.</p><p>接下来我们来看看，如何用给定的图形来给Launcher中的图标加边框.<br>继续从LauncherProvider开始分析.</p><p>本文讲的是开启Launcher的图标样式，实际上在Android O版本中，还可以开启系统级别的图标样式，Android O版本中系统图标样式是由 config_icon_mask 这个属性值控制,请参考<a href="https://sspai.com/post/41011" target="_blank" rel="noopener">为 Android 8.0 强制开启全局圆形图标规范</a><br>config_icon_mask 是在AdaptiveIconDrawable.java里面实现，也就是自适应图标，这个也是Android8.0的一个新功能.</p><h1 id="模拟未接来电"><a href="#模拟未接来电" class="headerlink" title="模拟未接来电"></a>模拟未接来电</h1><p>方式一：分别读取电话和短信数据库中的相应字段<br>方式二：从通知里面获取(Android8.0的做法)</p><p><a href="https://source.android.google.cn/devices/tech/connect/call-notification" target="_blank" rel="noopener">Android 7.0版本中的来电</a><br>通过查询发现MissCall这个通知是在Dialer里面发出，跟踪到MissedCallNotificationReceiver.java和MissedCallNotifier.java的代码，发现此通知是在收到<br>  public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = “android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION”;<br>  这样的广播后发出.当然这个广播还会带两个参数，未接来电号码和未接来电个数.</p><p>Launcher的功能是需要显示未接来电个数就行.所以有两种方法来实现<br>方法一：模拟发送这个广播<br>方法二：调用Telecommon里面发送这个广播的方法</p><p>首先看方法一：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sendNotificationThroughDefaultDialer</span><span class="params">(CallInfo callInfo, UserHandle userHandle)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> count = mMissedCallCounts.get(userHandle).get();</span><br><span class="line">    Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle)</span><br><span class="line">        .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)</span><br><span class="line">        .putExtra(TelecomManager.EXTRA_CLEAR_MISSED_CALLS_INTENT,</span><br><span class="line">                createClearMissedCallsPendingIntent(userHandle))</span><br><span class="line">        .putExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, count)</span><br><span class="line">        .putExtra(TelecomManager.EXTRA_NOTIFICATION_PHONE_NUMBER,</span><br><span class="line">                callInfo == <span class="keyword">null</span> ? <span class="keyword">null</span> : callInfo.getPhoneNumber());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (count == <span class="number">1</span> &amp;&amp; callInfo != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">final</span> Uri handleUri = callInfo.getHandle();</span><br><span class="line">        String handle = handleUri == <span class="keyword">null</span> ? <span class="keyword">null</span> : handleUri.getSchemeSpecificPart();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!TextUtils.isEmpty(handle) &amp;&amp; !TextUtils.equals(handle,</span><br><span class="line">                mContext.getString(R.string.handle_restricted))) &#123;</span><br><span class="line">            intent.putExtra(TelecomManager.EXTRA_CALL_BACK_INTENT,</span><br><span class="line">                    createCallBackPendingIntent(handleUri, userHandle));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    Log.w(<span class="keyword">this</span>, <span class="string">"Showing missed calls through default dialer."</span>);</span><br><span class="line">    mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="代码树"><a href="#代码树" class="headerlink" title="代码树"></a>代码树</h1><p>资源模块代码结构树如下：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br></pre></td><td class="code"><pre><span class="line">├── anim</span><br><span class="line">│   ├── hide_popup_window.xml</span><br><span class="line">│   ├── no_anim.xml</span><br><span class="line">│   ├── show_popup_window.xml</span><br><span class="line">│   └── task_open_enter.xml</span><br><span class="line">├── animator</span><br><span class="line">│   ├── all_apps_fastscroll_icon_anim.xml</span><br><span class="line">│   ├── discovery_bounce.xml</span><br><span class="line">│   └── overview_button_anim.xml</span><br><span class="line">├── animator-v23</span><br><span class="line">│   └── discovery_bounce.xml</span><br><span class="line">├── color-v24</span><br><span class="line">│   ├── all_apps_bg_hand_fill_dark.xml</span><br><span class="line">│   └── all_apps_bg_hand_fill.xml</span><br><span class="line">├── drawable</span><br><span class="line">│   ├── all_apps_button_icon.xml</span><br><span class="line">│   ├── all_apps_divider.xml</span><br><span class="line">│   ├── all_apps_search_divider.xml</span><br><span class="line">│   ├── all_apps_search_hint.xml</span><br><span class="line">│   ├── bg_celllayout.xml</span><br><span class="line">│   ├── deep_shortcuts_drag_handle.xml</span><br><span class="line">│   ├── gutter_horizontal.xml</span><br><span class="line">│   ├── horizontal_ellipsis.xml</span><br><span class="line">│   ├── ic_all_apps_bg_hand.xml</span><br><span class="line">│   ├── ic_all_apps_bg_icon_1.xml</span><br><span class="line">│   ├── ic_all_apps_bg_icon_2.xml</span><br><span class="line">│   ├── ic_all_apps_bg_icon_3.xml</span><br><span class="line">│   ├── ic_all_apps_bg_icon_4.xml</span><br><span class="line">│   ├── ic_allapps_search.xml</span><br><span class="line">│   ├── ic_info_no_shadow.xml</span><br><span class="line">│   ├── ic_instant_app_badge.xml</span><br><span class="line">│   ├── ic_launcher_home.xml</span><br><span class="line">│   ├── ic_remove_no_shadow.xml</span><br><span class="line">│   ├── ic_setting.xml</span><br><span class="line">│   ├── ic_star_rating.xml</span><br><span class="line">│   ├── ic_uninstall_no_shadow.xml</span><br><span class="line">│   ├── ic_wallpaper.xml</span><br><span class="line">│   ├── ic_warning.xml</span><br><span class="line">│   ├── ic_widget.xml</span><br><span class="line">│   ├── pending_widget_bg.xml</span><br><span class="line">│   ├── round_rect_primary.xml</span><br><span class="line">│   └── widget_internal_focus_bg.xml</span><br><span class="line">├── drawable-hdpi</span><br><span class="line">...</span><br><span class="line">│   ├── widget_resize_frame.9.png</span><br><span class="line">│   ├── widget_resize_shadow.9.png</span><br><span class="line">│   └── workspace_bg.9.png</span><br><span class="line">├── drawable-mdpi</span><br><span class="line">│   ├── check_theme.png</span><br><span class="line">│   ├── ic_allapps.png</span><br><span class="line">│   ├── ic_allapps_pressed.png</span><br><span class="line">│   ├── ic_pageindicator_add_current.png</span><br><span class="line">│   ├── ic_pageindicator_add.png</span><br><span class="line">│   ├── ic_pageindicator_current.png</span><br><span class="line">│   ├── ic_pageindicator_default.png</span><br><span class="line">│   ├── ic_widget_resize_handle.png</span><br><span class="line">│   ├── widget_resize_frame.9.png</span><br><span class="line">│   ├── widget_resize_shadow.9.png</span><br><span class="line">│   └── workspace_bg.9.png</span><br><span class="line">├── drawable-v24</span><br><span class="line">│   ├── ic_info_shadow.xml</span><br><span class="line">│   ├── ic_remove_shadow.xml</span><br><span class="line">│   └── ic_uninstall_shadow.xml</span><br><span class="line">├── drawable-v26</span><br><span class="line">│   ├── adaptive_icon_drawable_wrapper.xml</span><br><span class="line">│   └── ic_launcher_home.xml</span><br><span class="line">├── drawable-xhdpi</span><br><span class="line">│   ├── check_theme.png</span><br><span class="line">│   ├── ic_allapps.png</span><br><span class="line">│   ├── ic_allapps_pressed.png</span><br><span class="line">│   ├── ic_pageindicator_add_current.png</span><br><span class="line">│   ├── ic_pageindicator_add.png</span><br><span class="line">│   ├── ic_pageindicator_current.png</span><br><span class="line">│   ├── ic_pageindicator_default.png</span><br><span class="line">│   ├── ic_widget_resize_handle.png</span><br><span class="line">│   ├── widget_resize_frame.9.png</span><br><span class="line">│   ├── widget_resize_shadow.9.png</span><br><span class="line">│   └── workspace_bg.9.png</span><br><span class="line">├── drawable-xxhdpi</span><br><span class="line">│   ├── check_theme.png</span><br><span class="line">│   ├── ic_allapps.png</span><br><span class="line">│   ├── ic_allapps_pressed.png</span><br><span class="line">│   ├── ic_pageindicator_add_current.png</span><br><span class="line">│   ├── ic_pageindicator_add.png</span><br><span class="line">│   ├── ic_pageindicator_current.png</span><br><span class="line">│   ├── ic_pageindicator_default.png</span><br><span class="line">│   ├── ic_widget_resize_handle.png</span><br><span class="line">│   ├── widget_resize_frame.9.png</span><br><span class="line">│   ├── widget_resize_shadow.9.png</span><br><span class="line">│   └── workspace_bg.9.png</span><br><span class="line">├── drawable-xxxhdpi</span><br><span class="line">│   ├── check_theme.png</span><br><span class="line">│   ├── ic_widget_resize_handle.png</span><br><span class="line">│   ├── widget_resize_frame.9.png</span><br><span class="line">│   ├── widget_resize_shadow.9.png</span><br><span class="line">│   └── workspace_bg.9.png</span><br><span class="line">├── interpolator</span><br><span class="line">│   ├── decelerate_quart.xml</span><br><span class="line">│   ├── decelerate_quint.xml</span><br><span class="line">│   ├── disco_bounce.xml</span><br><span class="line">│   ├── folder_interpolator.xml</span><br><span class="line">│   ├── large_folder_preview_item_close_interpolator.xml</span><br><span class="line">│   └── large_folder_preview_item_open_interpolator.xml</span><br><span class="line">├── layout</span><br><span class="line">│   ├── add_item_confirmation_activity.xml</span><br><span class="line">│   ├── all_apps_button.xml</span><br><span class="line">│   ├── all_apps_discovery_item.xml</span><br><span class="line">│   ├── all_apps_discovery_loading_divider.xml</span><br><span class="line">│   ├── all_apps_divider.xml</span><br><span class="line">│   ├── all_apps_empty_search.xml</span><br><span class="line">│   ├── all_apps_fast_scroller.xml</span><br><span class="line">│   ├── all_apps_icon.xml</span><br><span class="line">│   ├── all_apps_search_market.xml</span><br><span class="line">│   ├── all_apps.xml</span><br><span class="line">│   ├── app_icon.xml</span><br><span class="line">│   ├── appwidget_error.xml</span><br><span class="line">│   ├── appwidget_not_ready.xml</span><br><span class="line">│   ├── app_widget_resize_frame.xml</span><br><span class="line">│   ├── deep_shortcut.xml</span><br><span class="line">│   ├── drop_target_bar_horz.xml</span><br><span class="line">│   ├── drop_target_bar_vert.xml</span><br><span class="line">│   ├── folder_application.xml</span><br><span class="line">│   ├── folder_icon.xml</span><br><span class="line">│   ├── folder_page.xml</span><br><span class="line">│   ├── gradient_bg.xml</span><br><span class="line">│   ├── hotseat.xml</span><br><span class="line">│   ├── notification_footer.xml</span><br><span class="line">│   ├── notification_main.xml</span><br><span class="line">│   ├── notification_pref_warning.xml</span><br><span class="line">│   ├── notification.xml</span><br><span class="line">│   ├── overview_panel.xml</span><br><span class="line">│   ├── page_indicator_marker.xml</span><br><span class="line">│   ├── page_indicator_point.xml</span><br><span class="line">│   ├── page_indicator.xml</span><br><span class="line">│   ├── popup_container.xml</span><br><span class="line">│   ├── popup_window.xml</span><br><span class="line">│   ├── qsb_default_view.xml</span><br><span class="line">│   ├── search_container_all_apps.xml</span><br><span class="line">│   ├── search_container_workspace.xml</span><br><span class="line">│   ├── shortcuts_item.xml</span><br><span class="line">│   ├── system_shortcut_icon_only.xml</span><br><span class="line">│   ├── system_shortcut_icons.xml</span><br><span class="line">│   ├── system_shortcut.xml</span><br><span class="line">│   ├── theme_item.xml</span><br><span class="line">│   ├── theme.xml</span><br><span class="line">│   ├── user_folder_icon_normalized.xml</span><br><span class="line">│   ├── user_folder.xml</span><br><span class="line">│   ├── widget_cell_content.xml</span><br><span class="line">│   ├── widget_cell.xml</span><br><span class="line">│   ├── widget_list_divider.xml</span><br><span class="line">│   ├── widgets_bottom_sheet.xml</span><br><span class="line">│   ├── widgets_list_row_view.xml</span><br><span class="line">│   ├── widgets_scroll_container.xml</span><br><span class="line">│   ├── widgets_view.xml</span><br><span class="line">│   ├── workspace_screen.xml</span><br><span class="line">│   ├── zzz_dummy_widget.xml</span><br><span class="line">│   └── zzz_weight_watcher.xml</span><br><span class="line">├── layout-land</span><br><span class="line">│   ├── all_apps_fast_scroller.xml</span><br><span class="line">│   └── launcher.xml</span><br><span class="line">├── layout-port</span><br><span class="line">│   └── launcher.xml</span><br><span class="line">├── layout-sw720dp</span><br><span class="line">│   ├── all_apps_fast_scroller.xml</span><br><span class="line">│   └── launcher.xml</span><br><span class="line">├── mipmap-hdpi</span><br><span class="line">│   ├── ic_launcher_home_foreground.png</span><br><span class="line">│   └── ic_launcher_home.png</span><br><span class="line">├── mipmap-mdpi</span><br><span class="line">│   ├── ic_launcher_home_foreground.png</span><br><span class="line">│   └── ic_launcher_home.png</span><br><span class="line">├── mipmap-xhdpi</span><br><span class="line">│   ├── ic_launcher_home_foreground.png</span><br><span class="line">│   └── ic_launcher_home.png</span><br><span class="line">├── mipmap-xxhdpi</span><br><span class="line">│   ├── ic_launcher_home_foreground.png</span><br><span class="line">│   └── ic_launcher_home.png</span><br><span class="line">├── raw</span><br><span class="line">│   └── downgrade_schema.json</span><br><span class="line">├── values</span><br><span class="line">│   ├── arrays.xml</span><br><span class="line">│   ├── attrs.xml</span><br><span class="line">│   ├── bools.xml</span><br><span class="line">│   ├── colors.xml</span><br><span class="line">│   ├── config.xml</span><br><span class="line">│   ├── dimens.xml</span><br><span class="line">│   ├── drawables.xml</span><br><span class="line">│   ├── strings.xml</span><br><span class="line">│   └── styles.xml</span><br><span class="line">......</span><br><span class="line">├── values-zh-rCN</span><br><span class="line">│   └── strings.xml</span><br><span class="line">├── values-zh-rHK</span><br><span class="line">│   └── strings.xml</span><br><span class="line">├── values-zh-rTW</span><br><span class="line">│   └── strings.xml</span><br><span class="line">└── xml</span><br><span class="line">    ├── backupscheme.xml</span><br><span class="line">    ├── default_workspace_3x3.xml</span><br><span class="line">    ├── default_workspace_4x4.xml</span><br><span class="line">    ├── default_workspace_5x5.xml</span><br><span class="line">    ├── default_workspace_5x6.xml</span><br><span class="line">    ├── device_profiles.xml</span><br><span class="line">    ├── dw_phone_hotseat.xml</span><br><span class="line">    ├── dw_tablet_hotseat.xml</span><br><span class="line">    └── launcher_preferences.xml</span><br></pre></td></tr></table></figure></p><p>Java模块的代码结构树如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br></pre></td><td class="code"><pre><span class="line">├── src</span><br><span class="line">│   └── com</span><br><span class="line">│       └── android</span><br><span class="line">│           └── launcher3</span><br><span class="line">│               ├── AbstractFloatingView.java</span><br><span class="line">│               ├── accessibility</span><br><span class="line">│               │   ├── AccessibleDragListenerAdapter.java</span><br><span class="line">│               │   ├── DragAndDropAccessibilityDelegate.java</span><br><span class="line">│               │   ├── DragViewStateAnnouncer.java</span><br><span class="line">│               │   ├── FolderAccessibilityHelper.java</span><br><span class="line">│               │   ├── LauncherAccessibilityDelegate.java</span><br><span class="line">│               │   ├── OverviewAccessibilityDelegate.java</span><br><span class="line">│               │   ├── OverviewScreenAccessibilityDelegate.java</span><br><span class="line">│               │   ├── ShortcutMenuAccessibilityDelegate.java</span><br><span class="line">│               │   └── WorkspaceAccessibilityHelper.java</span><br><span class="line">│               ├── Alarm.java</span><br><span class="line">│               ├── allapps</span><br><span class="line">│               │   ├── AllAppsBackgroundDrawable.java</span><br><span class="line">│               │   ├── AllAppsCaretController.java</span><br><span class="line">│               │   ├── AllAppsContainerView.java</span><br><span class="line">│               │   ├── AllAppsFastScrollHelper.java</span><br><span class="line">│               │   ├── AllAppsGridAdapter.java</span><br><span class="line">│               │   ├── AllAppsRecyclerViewContainerView.java</span><br><span class="line">│               │   ├── AllAppsRecyclerView.java</span><br><span class="line">│               │   ├── AllAppsTransitionController.java</span><br><span class="line">│               │   ├── AlphabeticalAppsList.java</span><br><span class="line">│               │   ├── AppInfoComparator.java</span><br><span class="line">│               │   ├── LandscapeFastScroller.java</span><br><span class="line">│               │   ├── search</span><br><span class="line">│               │   │   ├── AllAppsSearchBarController.java</span><br><span class="line">│               │   │   ├── AppsSearchContainerLayout.java</span><br><span class="line">│               │   │   ├── DefaultAppSearchAlgorithm.java</span><br><span class="line">│               │   │   ├── HeaderElevationController.java</span><br><span class="line">│               │   │   └── SearchAlgorithm.java</span><br><span class="line">│               │   └── SearchUiManager.java</span><br><span class="line">│               ├── AllAppsList.java  </span><br><span class="line">│               ├── anim</span><br><span class="line">│               │   ├── AnimationLayerSet.java</span><br><span class="line">│               │   ├── CircleRevealOutlineProvider.java</span><br><span class="line">│               │   ├── PropertyListBuilder.java</span><br><span class="line">│               │   ├── PropertyResetListener.java</span><br><span class="line">│               │   ├── RevealOutlineAnimation.java</span><br><span class="line">│               │   ├── RoundedRectRevealOutlineProvider.java</span><br><span class="line">│               │   └── SpringAnimationHandler.java</span><br><span class="line">│               ├── AppFilter.java</span><br><span class="line">│               ├── AppInfo.java</span><br><span class="line">│               ├── AppWidgetResizeFrame.java</span><br><span class="line">│               ├── AppWidgetsRestoredReceiver.java</span><br><span class="line">│               ├── AutoInstallsLayout.java</span><br><span class="line">│               ├── badge</span><br><span class="line">│               │   ├── BadgeInfo.java</span><br><span class="line">│               │   ├── BadgeRenderer.java</span><br><span class="line">│               │   └── FolderBadgeInfo.java</span><br><span class="line">│               ├── BaseActivity.java</span><br><span class="line">│               ├── BaseContainerView.java</span><br><span class="line">│               ├── BaseRecyclerView.java</span><br><span class="line">│               ├── BubbleTextView.java</span><br><span class="line">│               ├── ButtonDropTarget.java</span><br><span class="line">│               ├── CellLayout.java</span><br><span class="line">│               ├── CheckLongPressHelper.java</span><br><span class="line">│               ├── ClickShadowView.java</span><br><span class="line">│               ├── compat</span><br><span class="line">│               │   ├── AlphabeticIndexCompat.java</span><br><span class="line">│               │   ├── AppWidgetManagerCompat.java</span><br><span class="line">│               │   ├── AppWidgetManagerCompatVL.java</span><br><span class="line">│               │   ├── AppWidgetManagerCompatVO.java</span><br><span class="line">│               │   ├── LauncherAppsCompat.java</span><br><span class="line">│               │   ├── LauncherAppsCompatVL.java</span><br><span class="line">│               │   ├── LauncherAppsCompatVO.java</span><br><span class="line">│               │   ├── PackageInstallerCompat.java</span><br><span class="line">│               │   ├── PackageInstallerCompatVL.java</span><br><span class="line">│               │   ├── ShortcutConfigActivityInfo.java</span><br><span class="line">│               │   ├── UserManagerCompat.java</span><br><span class="line">│               │   ├── UserManagerCompatVL.java</span><br><span class="line">│               │   ├── UserManagerCompatVM.java</span><br><span class="line">│               │   ├── UserManagerCompatVN.java</span><br><span class="line">│               │   ├── UserManagerCompatVNMr1.java</span><br><span class="line">│               │   ├── WallpaperColorsCompat.java</span><br><span class="line">│               │   ├── WallpaperManagerCompat.java</span><br><span class="line">│               │   ├── WallpaperManagerCompatVL.java</span><br><span class="line">│               │   └── WallpaperManagerCompatVOMR1.java</span><br><span class="line">│               ├── config</span><br><span class="line">│               │   └── BaseFlags.java</span><br><span class="line">│               ├── CustomAppWidget.java</span><br><span class="line">│               ├── DefaultLayoutParser.java</span><br><span class="line">│               ├── DeleteDropTarget.java</span><br><span class="line">│               ├── DeviceProfile.java</span><br><span class="line">│               ├── discovery</span><br><span class="line">│               │   ├── AppDiscoveryAppInfo.java</span><br><span class="line">│               │   ├── AppDiscoveryItem.java</span><br><span class="line">│               │   ├── AppDiscoveryItemView.java</span><br><span class="line">│               │   ├── AppDiscoveryUpdateState.java</span><br><span class="line">│               │   └── RatingView.java</span><br><span class="line">│               ├── dragndrop</span><br><span class="line">│               │   ├── AddItemActivity.java</span><br><span class="line">│               │   ├── BaseItemDragListener.java</span><br><span class="line">│               │   ├── DragController.java</span><br><span class="line">│               │   ├── DragDriver.java</span><br><span class="line">│               │   ├── DragLayer.java</span><br><span class="line">│               │   ├── DragOptions.java</span><br><span class="line">│               │   ├── DragView.java</span><br><span class="line">│               │   ├── FlingToDeleteHelper.java</span><br><span class="line">│               │   ├── FolderAdaptiveIcon.java</span><br><span class="line">│               │   ├── LivePreviewWidgetCell.java</span><br><span class="line">│               │   ├── PinItemDragListener.java</span><br><span class="line">│               │   ├── PinShortcutRequestActivityInfo.java</span><br><span class="line">│               │   ├── PinWidgetFlowHandler.java</span><br><span class="line">│               │   └── SpringLoadedDragController.java</span><br><span class="line">│               ├── DragSource.java</span><br><span class="line">│               ├── DropTargetBar.java</span><br><span class="line">│               ├── DropTarget.java</span><br><span class="line">│               ├── dynamicui</span><br><span class="line">│               │   ├── ColorExtractionAlgorithm.java</span><br><span class="line">│               │   ├── ColorExtractionService.java</span><br><span class="line">│               │   ├── ExtractedColors.java</span><br><span class="line">│               │   ├── ExtractionUtils.java</span><br><span class="line">│               │   └── WallpaperColorInfo.java</span><br><span class="line">│               ├── ExtendedEditText.java</span><br><span class="line">│               ├── FastBitmapDrawable.java</span><br><span class="line">│               ├── FirstFrameAnimatorHelper.java</span><br><span class="line">│               ├── FocusHelper.java</span><br><span class="line">│               ├── folder</span><br><span class="line">│               │   ├── ClippedFolderIconLayoutRule.java</span><br><span class="line">│               │   ├── FolderAnimationManager.java</span><br><span class="line">│               │   ├── FolderIcon.java</span><br><span class="line">│               │   ├── FolderIconPreviewVerifier.java</span><br><span class="line">│               │   ├── Folder.java</span><br><span class="line">│               │   ├── FolderPagedView.java</span><br><span class="line">│               │   ├── FolderPreviewItemAnim.java</span><br><span class="line">│               │   ├── PreviewBackground.java</span><br><span class="line">│               │   ├── PreviewImageView.java</span><br><span class="line">│               │   ├── PreviewItemDrawingParams.java</span><br><span class="line">│               │   ├── PreviewItemManager.java</span><br><span class="line">│               │   └── StackFolderIconLayoutRule.java</span><br><span class="line">│               ├── FolderInfo.java</span><br><span class="line">│               ├── graphics</span><br><span class="line">│               │   ├── DragPreviewProvider.java</span><br><span class="line">│               │   ├── DrawableFactory.java</span><br><span class="line">│               │   ├── FastScrollThumbDrawable.java</span><br><span class="line">│               │   ├── FixedScaleDrawable.java</span><br><span class="line">│               │   ├── GradientView.java</span><br><span class="line">│               │   ├── HolographicOutlineHelper.java</span><br><span class="line">│               │   ├── IconNormalizer.java</span><br><span class="line">│               │   ├── IconPalette.java</span><br><span class="line">│               │   ├── IconShapeOverride.java</span><br><span class="line">│               │   ├── LauncherIcons.java</span><br><span class="line">│               │   ├── PreloadIconDrawable.java</span><br><span class="line">│               │   ├── ShadowDrawable.java</span><br><span class="line">│               │   ├── ShadowGenerator.java</span><br><span class="line">│               │   ├── TintedDrawableSpan.java</span><br><span class="line">│               │   └── TriangleShape.java</span><br><span class="line">│               ├── Hotseat.java</span><br><span class="line">│               ├── IconCache.java</span><br><span class="line">│               ├── IconProvider.java</span><br><span class="line">│               ├── InfoDropTarget.java</span><br><span class="line">│               ├── InsettableFrameLayout.java</span><br><span class="line">│               ├── Insettable.java</span><br><span class="line">│               ├── InstallShortcutReceiver.java</span><br><span class="line">│               ├── InterruptibleInOutAnimator.java</span><br><span class="line">│               ├── InvariantDeviceProfile.java</span><br><span class="line">│               ├── ItemInfo.java</span><br><span class="line">│               ├── ItemInfoWithIcon.java</span><br><span class="line">│               ├── keyboard</span><br><span class="line">│               │   ├── CustomActionsPopup.java</span><br><span class="line">│               │   ├── FocusedItemDecorator.java</span><br><span class="line">│               │   ├── FocusIndicatorHelper.java</span><br><span class="line">│               │   └── ViewGroupFocusHelper.java</span><br><span class="line">│               ├── LauncherAnimUtils.java</span><br><span class="line">│               ├── LauncherAppState.java</span><br><span class="line">│               ├── LauncherAppWidgetHost.java</span><br><span class="line">│               ├── LauncherAppWidgetHostView.java</span><br><span class="line">│               ├── LauncherAppWidgetInfo.java</span><br><span class="line">│               ├── LauncherAppWidgetProviderInfo.java</span><br><span class="line">│               ├── LauncherBackupAgent.java</span><br><span class="line">│               ├── LauncherCallbacks.java</span><br><span class="line">│               ├── LauncherExterns.java</span><br><span class="line">│               ├── LauncherFiles.java</span><br><span class="line">│               ├── Launcher.java</span><br><span class="line">│               ├── LauncherModel.java</span><br><span class="line">│               ├── LauncherProviderChangeListener.java</span><br><span class="line">│               ├── LauncherProvider.java</span><br><span class="line">│               ├── LauncherRootView.java</span><br><span class="line">│               ├── LauncherScroller.java</span><br><span class="line">│               ├── LauncherSettings.java</span><br><span class="line">│               ├── LauncherStateTransitionAnimation.java</span><br><span class="line">│               ├── LogAccelerateInterpolator.java</span><br><span class="line">│               ├── LogDecelerateInterpolator.java</span><br><span class="line">│               ├── logging</span><br><span class="line">│               │   ├── DumpTargetWrapper.java</span><br><span class="line">│               │   ├── FileLog.java</span><br><span class="line">│               │   ├── LoggerUtils.java</span><br><span class="line">│               │   └── UserEventDispatcher.java</span><br><span class="line">│               ├── MainThreadExecutor.java</span><br><span class="line">│               ├── model</span><br><span class="line">│               │   ├── AddWorkspaceItemsTask.java</span><br><span class="line">│               │   ├── BaseModelUpdateTask.java</span><br><span class="line">│               │   ├── BgDataModel.java</span><br><span class="line">│               │   ├── CacheDataUpdatedTask.java</span><br><span class="line">│               │   ├── DbDowngradeHelper.java</span><br><span class="line">│               │   ├── GridSizeMigrationTask.java</span><br><span class="line">│               │   ├── LoaderCursor.java</span><br><span class="line">│               │   ├── LoaderResults.java</span><br><span class="line">│               │   ├── LoaderTask.java</span><br><span class="line">│               │   ├── ModelWriter.java</span><br><span class="line">│               │   ├── PackageInstallStateChangedTask.java</span><br><span class="line">│               │   ├── PackageItemInfo.java</span><br><span class="line">│               │   ├── PackageUpdatedTask.java</span><br><span class="line">│               │   ├── SdCardAvailableReceiver.java</span><br><span class="line">│               │   ├── ShortcutsChangedTask.java</span><br><span class="line">│               │   ├── UserLockStateChangedTask.java</span><br><span class="line">│               │   ├── WidgetItem.java</span><br><span class="line">│               │   └── WidgetsModel.java</span><br><span class="line">│               ├── notification</span><br><span class="line">│               │   ├── Interpolators.java</span><br><span class="line">│               │   ├── NotificationFooterLayout.java</span><br><span class="line">│               │   ├── NotificationInfo.java</span><br><span class="line">│               │   ├── NotificationItemView.java</span><br><span class="line">│               │   ├── NotificationKeyData.java</span><br><span class="line">│               │   ├── NotificationListener.java</span><br><span class="line">│               │   └── NotificationMainView.java</span><br><span class="line">│               ├── OnAlarmListener.java</span><br><span class="line">│               ├── OverviewButtonClickListener.java</span><br><span class="line">│               ├── PagedView.java</span><br><span class="line">│               ├── pageindicators</span><br><span class="line">│               │   ├── CaretDrawable.java</span><br><span class="line">│               │   ├── PageIndicatorCaretLandscape.java</span><br><span class="line">│               │   ├── PageIndicatorDots.java</span><br><span class="line">│               │   ├── PageIndicator.java</span><br><span class="line">│               │   ├── PageIndicatorLineCaret.java</span><br><span class="line">│               │   ├── PageIndicatorMarker.java</span><br><span class="line">│               │   └── PageIndicatorPoint.java</span><br><span class="line">│               ├── Partner.java</span><br><span class="line">│               ├── PendingAddItemInfo.java</span><br><span class="line">│               ├── PendingAppWidgetHostView.java</span><br><span class="line">│               ├── PinchAnimationManager.java</span><br><span class="line">│               ├── PinchThresholdManager.java</span><br><span class="line">│               ├── PinchToOverviewListener.java</span><br><span class="line">│               ├── popup</span><br><span class="line">│               │   ├── PopupContainerWithArrow.java</span><br><span class="line">│               │   ├── PopupDataProvider.java</span><br><span class="line">│               │   ├── PopupItemView.java</span><br><span class="line">│               │   ├── PopupPopulator.java</span><br><span class="line">│               │   └── SystemShortcut.java</span><br><span class="line">│               ├── PromiseAppInfo.java</span><br><span class="line">│               ├── provider</span><br><span class="line">│               │   ├── ImportDataTask.java</span><br><span class="line">│               │   ├── LauncherDbUtils.java</span><br><span class="line">│               │   ├── LossyScreenMigrationTask.java</span><br><span class="line">│               │   └── RestoreDbTask.java</span><br><span class="line">│               ├── qsb</span><br><span class="line">│               │   ├── QsbContainerView.java</span><br><span class="line">│               │   └── QsbWidgetHostView.java</span><br><span class="line">│               ├── SessionCommitReceiver.java</span><br><span class="line">│               ├── SettingsActivity.java</span><br><span class="line">│               ├── ShortcutAndWidgetContainer.java</span><br><span class="line">│               ├── ShortcutInfo.java</span><br><span class="line">│               ├── shortcuts</span><br><span class="line">│               │   ├── DeepShortcutManager.java</span><br><span class="line">│               │   ├── DeepShortcutTextView.java</span><br><span class="line">│               │   ├── DeepShortcutView.java</span><br><span class="line">│               │   ├── ShortcutCache.java</span><br><span class="line">│               │   ├── ShortcutDragPreviewProvider.java</span><br><span class="line">│               │   ├── ShortcutInfoCompat.java</span><br><span class="line">│               │   ├── ShortcutKey.java</span><br><span class="line">│               │   └── ShortcutsItemView.java</span><br><span class="line">│               ├── SimpleOnStylusPressListener.java</span><br><span class="line">│               ├── StylusEventHelper.java</span><br><span class="line">│               ├── testing</span><br><span class="line">│               │   ├── DummyWidget.java</span><br><span class="line">│               │   ├── LauncherExtension.java</span><br><span class="line">│               │   ├── MemoryDumpActivity.java</span><br><span class="line">│               │   ├── MemoryTracker.java</span><br><span class="line">│               │   ├── ToggleWeightWatcher.java</span><br><span class="line">│               │   └── WeightWatcher.java</span><br><span class="line">│               ├── theme</span><br><span class="line">│               │   ├── LocalThemeFragment.java</span><br><span class="line">│               │   ├── LocalThemeUtils.java</span><br><span class="line">│               │   ├── ProgressView.java</span><br><span class="line">│               │   └── ThemeUpdateTask.java</span><br><span class="line">│               ├── touch</span><br><span class="line">│               │   ├── OverScroll.java</span><br><span class="line">│               │   └── SwipeDetector.java</span><br><span class="line">│               ├── UninstallDropTarget.java</span><br><span class="line">│               ├── util</span><br><span class="line">│               │   ├── ActivityResultInfo.java</span><br><span class="line">│               │   ├── CellAndSpan.java</span><br><span class="line">│               │   ├── ComponentKey.java</span><br><span class="line">│               │   ├── ComponentKeyMapper.java</span><br><span class="line">│               │   ├── ConfigMonitor.java</span><br><span class="line">│               │   ├── ContentWriter.java</span><br><span class="line">│               │   ├── FlagOp.java</span><br><span class="line">│               │   ├── FlingAnimation.java</span><br><span class="line">│               │   ├── FocusLogic.java</span><br><span class="line">│               │   ├── GridOccupancy.java</span><br><span class="line">│               │   ├── InstantAppResolver.java</span><br><span class="line">│               │   ├── IOUtils.java</span><br><span class="line">│               │   ├── ItemInfoMatcher.java</span><br><span class="line">│               │   ├── LabelComparator.java</span><br><span class="line">│               │   ├── LogConfig.java</span><br><span class="line">│               │   ├── LongArrayMap.java</span><br><span class="line">│               │   ├── LooperExecutor.java</span><br><span class="line">│               │   ├── LooperIdleLock.java</span><br><span class="line">│               │   ├── ManagedProfileHeuristic.java</span><br><span class="line">│               │   ├── MultiHashMap.java</span><br><span class="line">│               │   ├── NoLocaleSqliteContext.java</span><br><span class="line">│               │   ├── PackageManagerHelper.java</span><br><span class="line">│               │   ├── PackageUserKey.java</span><br><span class="line">│               │   ├── ParcelableSparseArray.java</span><br><span class="line">│               │   ├── PendingRequestArgs.java</span><br><span class="line">│               │   ├── Preconditions.java</span><br><span class="line">│               │   ├── Provider.java</span><br><span class="line">│               │   ├── RunnableWithId.java</span><br><span class="line">│               │   ├── SettingsObserver.java</span><br><span class="line">│               │   ├── SQLiteCacheHelper.java</span><br><span class="line">│               │   ├── SystemUiController.java</span><br><span class="line">│               │   ├── TestingUtils.java</span><br><span class="line">│               │   ├── Themes.java</span><br><span class="line">│               │   ├── Thunk.java</span><br><span class="line">│               │   ├── TouchController.java</span><br><span class="line">│               │   ├── TransformingTouchDelegate.java</span><br><span class="line">│               │   ├── VerticalFlingDetector.java</span><br><span class="line">│               │   ├── ViewOnDrawExecutor.java</span><br><span class="line">│               │   └── WallpaperOffsetInterpolator.java</span><br><span class="line">│               ├── Utilities.java</span><br><span class="line">│               ├── views</span><br><span class="line">│               │   ├── ButtonPreference.java</span><br><span class="line">│               │   ├── DoubleShadowBubbleTextView.java</span><br><span class="line">│               │   └── RecyclerViewFastScroller.java</span><br><span class="line">│               ├── widget</span><br><span class="line">│               │   ├── PendingAddShortcutInfo.java</span><br><span class="line">│               │   ├── PendingAddWidgetInfo.java</span><br><span class="line">│               │   ├── PendingItemDragHelper.java</span><br><span class="line">│               │   ├── WidgetAddFlowHandler.java</span><br><span class="line">│               │   ├── WidgetCell.java</span><br><span class="line">│               │   ├── WidgetHostViewLoader.java</span><br><span class="line">│               │   ├── WidgetImageView.java</span><br><span class="line">│               │   ├── WidgetItemComparator.java</span><br><span class="line">│               │   ├── WidgetListRowEntry.java</span><br><span class="line">│               │   ├── WidgetsBottomSheet.java</span><br><span class="line">│               │   ├── WidgetsContainerView.java</span><br><span class="line">│               │   ├── WidgetsDiffReporter.java</span><br><span class="line">│               │   ├── WidgetsListAdapter.java</span><br><span class="line">│               │   ├── WidgetsRecyclerView.java</span><br><span class="line">│               │   └── WidgetsRowViewHolder.java</span><br><span class="line">│               ├── WidgetPreviewLoader.java</span><br><span class="line">│               ├── Workspace.java</span><br><span class="line">│               └── WorkspaceStateTransitionAnimation.java</span><br><span class="line">├── src_config</span><br><span class="line">│   └── com</span><br><span class="line">│       └── android</span><br><span class="line">│           └── launcher3</span><br><span class="line">│               └── BuildConfig.java</span><br><span class="line">├── src_flags</span><br><span class="line">│   └── com</span><br><span class="line">│       └── android</span><br><span class="line">│           └── launcher3</span><br><span class="line">│               └── config</span><br><span class="line">│                   └── FeatureFlags.java</span><br><span class="line">└── tests</span><br><span class="line">    ├── AndroidManifest-common.xml</span><br><span class="line">    │   ├── raw</span><br><span class="line">    │   │   ├── cache_data_updated_task_data.txt</span><br><span class="line">    │   │   ├── db_schema_v10.json</span><br><span class="line">    │   │   └── package_install_state_change_task_data.txt</span><br><span class="line">    │   └── xml</span><br><span class="line">    │       ├── appwidget_no_config.xml</span><br><span class="line">    │       └── appwidget_with_config.xml</span><br><span class="line">    └── src</span><br><span class="line">        └── com</span><br><span class="line">            └── android</span><br><span class="line">                └── launcher3</span><br><span class="line">                    ├── allapps</span><br><span class="line">                    │   └── search</span><br><span class="line">                    │       └── DefaultAppSearchAlgorithmTest.java</span><br><span class="line">                    ├── logging</span><br><span class="line">                    │   └── FileLogTest.java</span><br><span class="line">                    ├── model</span><br><span class="line">                    │   ├── AddWorkspaceItemsTaskTest.java</span><br><span class="line">                    │   ├── BaseModelUpdateTaskTestCase.java</span><br><span class="line">                    │   ├── CacheDataUpdatedTaskTest.java</span><br><span class="line">                    │   ├── DbDowngradeHelperTest.java</span><br><span class="line">                    │   ├── GridSizeMigrationTaskTest.java</span><br><span class="line">                    │   ├── LoaderCursorTest.java</span><br><span class="line">                    │   └── PackageInstallStateChangedTaskTest.java</span><br><span class="line">                    ├── popup</span><br><span class="line">                    │   └── PopupPopulatorTest.java</span><br><span class="line">                    ├── provider</span><br><span class="line">                    │   └── RestoreDbTaskTest.java</span><br><span class="line">                    ├── testcomponent</span><br><span class="line">                    │   ├── AppWidgetNoConfig.java</span><br><span class="line">                    │   ├── AppWidgetWithConfig.java</span><br><span class="line">                    │   ├── BaseTestingActivity.java</span><br><span class="line">                    │   ├── RequestPinItemActivity.java</span><br><span class="line">                    │   ├── TouchEventGenerator.java</span><br><span class="line">                    │   └── WidgetConfigActivity.java</span><br><span class="line">                    ├── touch</span><br><span class="line">                    │   └── SwipeDetectorTest.java</span><br><span class="line">                    ├── ui</span><br><span class="line">                    │   ├── AbstractLauncherUiTest.java</span><br><span class="line">                    │   ├── AllAppsAppLaunchTest.java</span><br><span class="line">                    │   ├── AllAppsIconToHomeTest.java</span><br><span class="line">                    │   ├── ShortcutsLaunchTest.java</span><br><span class="line">                    │   ├── ShortcutsToHomeTest.java</span><br><span class="line">                    │   └── widget</span><br><span class="line">                    │       ├── AddConfigWidgetTest.java</span><br><span class="line">                    │       ├── AddWidgetTest.java</span><br><span class="line">                    │       ├── BindWidgetTest.java</span><br><span class="line">                    │       └── RequestPinItemTest.java</span><br><span class="line">                    ├── util</span><br><span class="line">                    │   ├── Condition.java</span><br><span class="line">                    │   ├── FocusLogicTest.java</span><br><span class="line">                    │   ├── GridOccupancyTest.java</span><br><span class="line">                    │   ├── rule</span><br><span class="line">                    │   │   ├── LauncherActivityRule.java</span><br><span class="line">                    │   │   └── ShellCommandRule.java</span><br><span class="line">                    │   ├── TestLauncherProvider.java</span><br><span class="line">                    │   └── Wait.java</span><br><span class="line">                    └── widget</span><br><span class="line">                        └── WidgetsListAdapterTest.java</span><br></pre></td></tr></table></figure></p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/yanbober/article/details/50525559" target="_blank" rel="noopener">Android M Launcher3主流程源码浅析</a> ★★★★★</p><p><a href="https://blog.csdn.net/u011694328/article/details/51514076" target="_blank" rel="noopener">Android Launcher浅析(一)</a> workspace左右滑动处理</p><p><a href="https://blog.csdn.net/qianguming/article/details/42008629" target="_blank" rel="noopener">android launcher 全面解析</a> 可以参考一下</p><p><a href="https://blog.csdn.net/fu_kevin0606/article/details/54931704" target="_blank" rel="noopener">Android7.0 启动Launcher流程</a> Launcher启动流程可以参考</p><p><a href="http://www.lichengfeng.cn/archives/351.html" target="_blank" rel="noopener">Android M 6.0 Launcher3 动态改变日历图标</a> </p><p><a href="https://blog.csdn.net/xufeifandj/article/details/39851741" target="_blank" rel="noopener">Launcher 细说ItemInfo</a></p><p><a href="https://blog.csdn.net/weixin_37077539/article/details/54892406" target="_blank" rel="noopener">一步步客制化Android桌面（Launcher3）图标</a> 可以用此思路来更改主题</p><p><a href="https://www.cnblogs.com/mythou/tag/launcher/" target="_blank" rel="noopener">Android Launcher分析和修改 系列文章</a></p><p><a href="http://codemx.cn/2016/07/30/Launcher01/#" target="_blank" rel="noopener">墨香带你学Launcher之（一）- 概述 </a> 讲述了Launcher的布局界面等基本东西<br><a href="http://codemx.cn/2016/08/05/Launcher02/" target="_blank" rel="noopener">墨香带你学Launcher之（二）- 数据加载流程 </a><br>讲解了Launcher桌面图标的默认配置<br>Launcher从AMS启动的流程<br>Launcher的初始化流程<br>Launcher的数据加载<br><a href="http://codemx.cn/2016/08/14/Launcher03/" target="_blank" rel="noopener">墨香带你学Launcher之（三）- 绑定屏幕、图标、文件夹和Widget </a><br><a href="http://codemx.cn/2016/08/21/Launcher04/" target="_blank" rel="noopener">墨香带你学Launcher之（四）- 应用安装、更新、卸载时的数据加载 </a><br><a href="http://codemx.cn/2016/10/16/Launcher05/" target="_blank" rel="noopener">墨香带你学Launcher之（五）- Workspace滑动 </a><br><a href="http://codemx.cn/2016/11/21/Launcher06/" target="_blank" rel="noopener">墨香带你学Launcher之（六）- 拖拽 </a><br><a href="http://codemx.cn/2016/12/18/Launcher07/" target="_blank" rel="noopener">墨香带你学Launcher之（七）- 小部件的加载、添加以及大小调节 </a><br><a href="http://codemx.cn/2017/05/19/Launcher08/" target="_blank" rel="noopener">墨香带你学Launcher之（八）- 加载Icon、设置壁纸 </a></p><p><a href="https://blog.csdn.net/yin1031468524/article/details/62216934" target="_blank" rel="noopener">Android原生Launcher3简要分析</a></p><p><a href="https://blog.csdn.net/qq_30552095/article/details/80494770" target="_blank" rel="noopener">Android8.1 Launcher3 去掉抽屉(一）</a><br><a href="https://blog.csdn.net/qq_30552095/article/details/80500290" target="_blank" rel="noopener">Android8.1 Launcher3 去掉抽屉（二）</a><br><a href="https://blog.csdn.net/qq_30552095/article/details/80508449" target="_blank" rel="noopener">Android8.1 Launcher3 去掉抽屉（三）</a></p><p><a href="https://blog.csdn.net/qq_31012033/article/details/73289232" target="_blank" rel="noopener">Android 7.0 Launcher3 去掉应用抽屉</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Ongoing,待完成，Please wait.&lt;/p&gt;
&lt;h1 id=&quot;Launcher-概述&quot;&gt;&lt;a href=&quot;#Launcher-概述&quot; class=&quot;headerlink&quot; title=&quot;Launcher 概述&quot;&gt;&lt;/a&gt;Launcher 概述&lt;/h1&gt;&lt;p&gt;La
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Launcher" scheme="http://yoursite.com/categories/Android/Launcher/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Launcher" scheme="http://yoursite.com/tags/Launcher/"/>
    
  </entry>
  
  <entry>
    <title>Android中ActivityManagerService解析</title>
    <link href="http://yoursite.com/Android-ActivityManagerService-Analysis/"/>
    <id>http://yoursite.com/Android-ActivityManagerService-Analysis/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-08-08T02:28:51.155Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>AMS是Android中最核心的服务，主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作，其职责与操作系统中的进程管理和调度模块相类似，因此它在Android中非常重要。</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8170307" target="_blank" rel="noopener">Android应用程序窗口（Activity）实现框架简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8201936" target="_blank" rel="noopener">Android应用程序窗口（Activity）的运行上下文环境（Context）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8223770" target="_blank" rel="noopener">Android应用程序窗口（Activity）的窗口对象（Window）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8245546" target="_blank" rel="noopener">Android应用程序窗口（Activity）的视图对象（View）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8275938" target="_blank" rel="noopener">Android应用程序窗口（Activity）与WindowManagerService服务的连接过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8303098" target="_blank" rel="noopener">Android应用程序窗口（Activity）的绘图表面（Surface）的创建过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8372924" target="_blank" rel="noopener">Android应用程序窗口（Activity）的测量（Measure）、布局（Layout）和绘制（Draw）过程分析</a></p><p><a href="http://wiki.jikexueyuan.com/project/deep-android-v2/activity.html" target="_blank" rel="noopener">第6章 深入理解ActivityManagerService</a></p><p><a href="https://blog.csdn.net/gaugamela/article/details/53515680" target="_blank" rel="noopener">Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;AMS是Android中最核心的服务，主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作，其职责与操作系统中的进程管理和调
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="ActivityManagerService" scheme="http://yoursite.com/categories/Android/ActivityManagerService/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="ActivityManagerService" scheme="http://yoursite.com/tags/ActivityManagerService/"/>
    
      <category term="AMS" scheme="http://yoursite.com/tags/AMS/"/>
    
  </entry>
  
  <entry>
    <title>Android匿名共享内存Ashmem解析</title>
    <link href="http://yoursite.com/Android-Ashmem-Analysis/"/>
    <id>http://yoursite.com/Android-Ashmem-Analysis/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-07-04T13:55:09.749Z</updated>
    
    <content type="html"><![CDATA[<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6651971" target="_blank" rel="noopener">Android系统匿名共享内存Ashmem（Anonymous Shared Memory）简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6664554" target="_blank" rel="noopener">Android系统匿名共享内存Ashmem（Anonymous Shared Memory）驱动程序源代码分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6666491" target="_blank" rel="noopener">Android系统匿名共享内存Ashmem（Anonymous Shared Memory）在进程间共享的原理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/6939890" target="_blank" rel="noopener">Android系统匿名共享内存（Anonymous Shared Memory）C++调用接口分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;参考资料&quot;&gt;&lt;a href=&quot;#参考资料&quot; class=&quot;headerlink&quot; title=&quot;参考资料&quot;&gt;&lt;/a&gt;参考资料&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/luoshengyang/article/details/89
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="Ashmem" scheme="http://yoursite.com/categories/Android/Ashmem/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="Ashmem" scheme="http://yoursite.com/tags/Ashmem/"/>
    
  </entry>
  
  <entry>
    <title>Android中SystemUI解析</title>
    <link href="http://yoursite.com/Android-SystemUI-Analysis/"/>
    <id>http://yoursite.com/Android-SystemUI-Analysis/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-09-10T12:17:45.834Z</updated>
    
    <content type="html"><![CDATA[<p>还未完成，待继续补充.</p><h1 id="SystemUI概述"><a href="#SystemUI概述" class="headerlink" title="SystemUI概述"></a>SystemUI概述</h1><p>Android8.1.0 SystemUI代码统计如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">Language                     files          blank        comment           code</span><br><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">Java                           892          23725          27078         143796</span><br><span class="line">XML                           1318           4337          20556          91781</span><br><span class="line">Python                           1             25             10            130</span><br><span class="line">Bourne Shell                     1              1              0             13</span><br><span class="line">-------------------------------------------------------------------------------</span><br><span class="line">SUM:                          2212          28088          47644         235720</span><br><span class="line">-------------------------------------------------------------------------------</span><br></pre></td></tr></table></figure></p><p>从Android7.0开始，把Keyguard模块的代码也一起放到了SystemUI模块中，从代码统计来看，SystemUI中Java代码大概有14万行.<br>实际SystemUI与Framework有很多的交互,还有很多的代码是在Framework的其他模块里面.</p><p>4.4版本SystemUI中SystemBar启动流程如下：<br><img src="/Android-SystemUI-Analysis/SystemUI-Start.png" alt="&quot;SystemUI启动流程分析&quot;"></p><h1 id="主要类功能初步解析"><a href="#主要类功能初步解析" class="headerlink" title="主要类功能初步解析"></a>主要类功能初步解析</h1><h2 id="ImmersiveModeConfirmation-java"><a href="#ImmersiveModeConfirmation-java" class="headerlink" title="ImmersiveModeConfirmation.java"></a>ImmersiveModeConfirmation.java</h2><p>首次隐藏状态栏和导航栏，进入沉浸模式是的提示</p><h1 id="SystemUI包含的服务"><a href="#SystemUI包含的服务" class="headerlink" title="SystemUI包含的服务"></a>SystemUI包含的服务</h1><p>SystemUI通过java启动的服务有13个，分别是：<br>1.TunerService 系统界面调谐器<br>    系统界面调制器是安卓M加入的，当用户长按下拉菜单的齿轮卡3秒中就可以在setting app里加入界面调制器。<br>可以对状态栏、导航栏、勿扰模式以及锁屏界面进行局部定制.</p><p>2.KeyguardViewMediator<br>    锁屏管理</p><p>3.Recents<br>    最近应用模块,</p><p>4.VolumeUI<br>    音量的调节</p><p>5.Divider</p><p>6.SystemBars<br>7.StorageNotification<br>8.PowerUI<br>9.RingtonePlayer<br>10.KeyboardUI<br>11.PipUI<br>12.ShortcutKeyDispatcher<br>13.VendorServices</p><p>通过xml启动的服务有6个，分别是：<br>1.SystemUIService<br>2.SystemUISecondaryUserService<br>3.TakeScreenshotService<br>4.ImageWallpaper<br>5.RecentsSystemUserService<br>6.DessertCaseDream<br>7.KeyguardService<br>8.DozeService</p><p>先以KeyguardViewMediator服务启动流程图为例进行分析,其它服务启动顺序与这个类似，只是功能不同而已。<br><img src="/Android-SystemUI-Analysis/KeyguardViewMediator_start.png" alt="&quot;KeyguardViewMediator服务启动流程分析&quot;"></p><p>SystemBar的启动流程图如下：<br><img src="/Android-SystemUI-Analysis/SystemBar-Start.png" alt="&quot;SystemBar服务启动流程分析&quot;"><br>PhoneStatusBar.java中的createAndAddWindows()会初始化并且加载SystemUI的绝大多数界面，包括statusbar与navigationbar，以及初始化QSHostView。</p><h1 id="SystemUI的功能模块"><a href="#SystemUI的功能模块" class="headerlink" title="SystemUI的功能模块"></a>SystemUI的功能模块</h1><p>SystemUI的功能模块：<br>a)Keyguard<br>锁屏和壁纸<br>b)StatusBar:电量、时间、SIM卡状态和信号、WIFI、Bluetooth、数据连接状态<br>通知、<br>c)NavigationBar<br>d)Shortcut<br>截图和录屏<br>e)Recents<br>f)PipUI<br>g)Tuner<br>h)RingtonePlayer</p><h1 id="SystemUI重难点"><a href="#SystemUI重难点" class="headerlink" title="SystemUI重难点"></a>SystemUI重难点</h1><ol><li>滑动事件的处理</li></ol><p>2.</p><h1 id="状态栏"><a href="#状态栏" class="headerlink" title="状态栏"></a>状态栏</h1><h2 id="锁屏状态栏"><a href="#锁屏状态栏" class="headerlink" title="锁屏状态栏"></a>锁屏状态栏</h2><h2 id="状态栏反色"><a href="#状态栏反色" class="headerlink" title="状态栏反色"></a>状态栏反色</h2><h2 id="沉浸式状态栏"><a href="#沉浸式状态栏" class="headerlink" title="沉浸式状态栏"></a>沉浸式状态栏</h2><h2 id="信号图标的刷新"><a href="#信号图标的刷新" class="headerlink" title="信号图标的刷新"></a>信号图标的刷新</h2><h2 id="凹口处理"><a href="#凹口处理" class="headerlink" title="凹口处理"></a>凹口处理</h2><h1 id="导航栏-NavigationBar"><a href="#导航栏-NavigationBar" class="headerlink" title="导航栏 NavigationBar"></a>导航栏 NavigationBar</h1><p>导航栏中的Back键和HOME键的事件处理：<br><a href="https://blog.csdn.net/span76/article/details/48441971" target="_blank" rel="noopener">https://blog.csdn.net/span76/article/details/48441971</a><br>Recent键的处理呢？<br>frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java<br>中的onRecentsClick函数处理Recent键</p><h2 id="导航栏的加载流程"><a href="#导航栏的加载流程" class="headerlink" title="导航栏的加载流程"></a>导航栏的加载流程</h2><blockquote><p>基于Android8.1代码<br>对于NavigationBar的加载从PhoneWindowManager开始讲起，PhoneWindowManager主要用于处理几个实体按键或者虚拟按键(Home/Back/Recent/Power/Volume up/Volume down)等按键事件,包括音量加减，截图，长按Power调出关机界面，长按Home/Back/Recent键，短按Home/Back/Recent/Power键. 显示点击点的位置.BugReport<br>以及SystemUI的NavigationBar和StatusBar的加载.手势，方向，HDMI，<br>StatusBar,NavigationBar,Wallpaper，文字输入法和语言输入法等窗口的绘制<br>显示开机界面中的Android正在启动.</p></blockquote><p>在PhoneWindowManager.java中与StatusBar和NavigationBar相关的代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PhoneWindowManager</span> <span class="keyword">implements</span> <span class="title">WindowManagerPolicy</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">"WindowManager"</span>;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">boolean</span> mSafeMode;</span><br><span class="line">    WindowState mStatusBar = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> mStatusBarHeight;</span><br><span class="line">    WindowState mNavigationBar = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">boolean</span> mHasNavigationBar = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">boolean</span> mNavigationBarCanMove = <span class="keyword">false</span>; <span class="comment">// can the navigation bar ever move to the side?</span></span><br><span class="line">    <span class="keyword">int</span> mNavigationBarPosition = NAV_BAR_BOTTOM;</span><br><span class="line">    <span class="keyword">int</span>[] mNavigationBarHeightForRotationDefault = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">4</span>];</span><br><span class="line">    <span class="keyword">int</span>[] mNavigationBarWidthForRotationDefault = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">4</span>];</span><br><span class="line">    <span class="keyword">int</span>[] mNavigationBarHeightForRotationInCarMode = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">4</span>];</span><br><span class="line">    <span class="keyword">int</span>[] mNavigationBarWidthForRotationInCarMode = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">4</span>];</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">boolean</span> mForceStatusBar;</span><br><span class="line">    <span class="keyword">boolean</span> mForceStatusBarFromKeyguard;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> mForceStatusBarTransparent;</span><br><span class="line">    <span class="keyword">int</span> mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> StatusBarController mStatusBarController = <span class="keyword">new</span> StatusBarController();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> BarController mNavigationBarController = <span class="keyword">new</span> BarController(<span class="string">"NavigationBar"</span>,</span><br><span class="line">            View.NAVIGATION_BAR_TRANSIENT,</span><br><span class="line">            View.NAVIGATION_BAR_UNHIDE,</span><br><span class="line">            View.NAVIGATION_BAR_TRANSLUCENT,</span><br><span class="line">            StatusBarManager.WINDOW_NAVIGATION_BAR,</span><br><span class="line">            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,</span><br><span class="line">            View.NAVIGATION_BAR_TRANSPARENT);</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">private</span> ImmersiveModeConfirmation mImmersiveModeConfirmation;<span class="comment">//沉浸模式</span></span><br><span class="line">    ...</span><br><span class="line">    <span class="function">IStatusBarService <span class="title">getStatusBarService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mServiceAquireLock) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mStatusBarService == <span class="keyword">null</span>) &#123;</span><br><span class="line">                mStatusBarService = IStatusBarService.Stub.asInterface(</span><br><span class="line">                        ServiceManager.getService(<span class="string">"statusbar"</span>));</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> mStatusBarService;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">StatusBarManagerInternal <span class="title">getStatusBarManagerInternal</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mServiceAquireLock) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mStatusBarManagerInternal == <span class="keyword">null</span>) &#123;</span><br><span class="line">                mStatusBarManagerInternal =</span><br><span class="line">                        LocalServices.getService(StatusBarManagerInternal.class);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> mStatusBarManagerInternal;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/** &#123;<span class="doctag">@inheritDoc</span>&#125; */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(Context context, IWindowManager windowManager,</span></span></span><br><span class="line"><span class="function"><span class="params">            WindowManagerFuncs windowManagerFuncs)</span> </span>&#123;</span><br><span class="line">        mContext = context;</span><br><span class="line">        mWindowManager = windowManager;</span><br><span class="line">        mWindowManagerFuncs = windowManagerFuncs;</span><br><span class="line">        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);</span><br><span class="line">        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);</span><br><span class="line">        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);</span><br><span class="line">        mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);</span><br><span class="line">        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);</span><br><span class="line">        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);</span><br><span class="line">        mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);</span><br><span class="line">        mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);</span><br><span class="line">        mAccessibilityShortcutController =</span><br><span class="line">                <span class="keyword">new</span> AccessibilityShortcutController(mContext, <span class="keyword">new</span> Handler(), mCurrentUserId);</span><br><span class="line">        <span class="comment">// Init display burn-in protection</span></span><br><span class="line">        <span class="keyword">boolean</span> burnInProtectionEnabled = context.getResources().getBoolean(</span><br><span class="line">                com.android.internal.R.bool.config_enableBurnInProtection);</span><br><span class="line">        <span class="comment">// Allow a system property to override this. Used by developer settings.</span></span><br><span class="line">        <span class="keyword">boolean</span> burnInProtectionDevMode =</span><br><span class="line">                SystemProperties.getBoolean(<span class="string">"persist.debug.force_burn_in"</span>, <span class="keyword">false</span>);</span><br><span class="line">        ...</span><br><span class="line">        readConfigurationDependentBehaviors();</span><br><span class="line">        ...</span><br><span class="line">        mImmersiveModeConfirmation = <span class="keyword">new</span> ImmersiveModeConfirmation(mContext);</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Read values from config.xml that may be overridden depending on</span></span><br><span class="line"><span class="comment">     * the configuration of the device.</span></span><br><span class="line"><span class="comment">     * eg. Disable long press on home goes to recents on sw600dp.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">readConfigurationDependentBehaviors</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> Resources res = mContext.getResources();</span><br><span class="line">        ...</span><br><span class="line">        mNavBarOpacityMode = res.getInteger(</span><br><span class="line">                com.android.internal.R.integer.config_navBarOpacityMode);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setInitialDisplaySize</span><span class="params">(Display display, <span class="keyword">int</span> width, <span class="keyword">int</span> height, <span class="keyword">int</span> density)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// This method might be called before the policy has been fully initialized</span></span><br><span class="line">        <span class="comment">// or for other displays we don't care about.</span></span><br><span class="line">        <span class="comment">// TODO(multi-display): Define policy for secondary displays.</span></span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// Allow the navigation bar to move on non-square small devices (phones).</span></span><br><span class="line">        mNavigationBarCanMove = width != height &amp;&amp; shortSizeDp &lt; <span class="number">600</span>;</span><br><span class="line"></span><br><span class="line">        mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Allow a system property to override this. Used by the emulator.</span></span><br><span class="line">        <span class="comment">// See also hasNavigationBar().</span></span><br><span class="line">        String navBarOverride = SystemProperties.get(<span class="string">"qemu.hw.mainkeys"</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"1"</span>.equals(navBarOverride)) &#123;</span><br><span class="line">            mHasNavigationBar = <span class="keyword">false</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"0"</span>.equals(navBarOverride)) &#123;</span><br><span class="line">            mHasNavigationBar = <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">canHideNavigationBar</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mHasNavigationBar;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">adjustWindowParamsLw</span><span class="params">(WindowManager.LayoutParams attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (attrs.type) &#123;</span><br><span class="line">            <span class="keyword">case</span> TYPE_SYSTEM_OVERLAY:</span><br><span class="line">            <span class="keyword">case</span> TYPE_SECURE_SYSTEM_OVERLAY:</span><br><span class="line">                <span class="comment">// These types of windows can't receive input events.</span></span><br><span class="line">                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE</span><br><span class="line">                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;</span><br><span class="line">                attrs.flags &amp;= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> TYPE_STATUS_BAR:</span><br><span class="line"></span><br><span class="line">                <span class="comment">// If the Keyguard is in a hidden state (occluded by another window), we force to</span></span><br><span class="line">                <span class="comment">// remove the wallpaper and keyguard flag so that any change in-flight after setting</span></span><br><span class="line">                <span class="comment">// the keyguard as occluded wouldn't set these flags again.</span></span><br><span class="line">                <span class="comment">// See &#123;@link #processKeyguardSetHiddenResultLw&#125;.</span></span><br><span class="line">                <span class="keyword">if</span> (mKeyguardOccluded) &#123;</span><br><span class="line">                    attrs.flags &amp;= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;</span><br><span class="line">                    attrs.privateFlags &amp;= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (attrs.type != TYPE_STATUS_BAR) &#123;</span><br><span class="line">            <span class="comment">// The status bar is the only window allowed to exhibit keyguard behavior.</span></span><br><span class="line">            attrs.privateFlags &amp;= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">    ....</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onConfigurationChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// TODO(multi-display): Define policy for secondary displays.</span></span><br><span class="line">        Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();</span><br><span class="line">        <span class="keyword">final</span> Resources res = uiContext.getResources();</span><br><span class="line"></span><br><span class="line">        mStatusBarHeight =</span><br><span class="line">                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Height of the navigation bar when presented horizontally at bottom</span></span><br><span class="line">        mNavigationBarHeightForRotationDefault[mPortraitRotation] =</span><br><span class="line">        mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =</span><br><span class="line">                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);</span><br><span class="line">        mNavigationBarHeightForRotationDefault[mLandscapeRotation] =</span><br><span class="line">        mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(</span><br><span class="line">                com.android.internal.R.dimen.navigation_bar_height_landscape);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Width of the navigation bar when presented vertically along one side</span></span><br><span class="line">        mNavigationBarWidthForRotationDefault[mPortraitRotation] =</span><br><span class="line">        mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =</span><br><span class="line">        mNavigationBarWidthForRotationDefault[mLandscapeRotation] =</span><br><span class="line">        mNavigationBarWidthForRotationDefault[mSeascapeRotation] =</span><br><span class="line">                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (ALTERNATE_CAR_MODE_NAV_SIZE) &#123;</span><br><span class="line">            <span class="comment">// Height of the navigation bar when presented horizontally at bottom</span></span><br><span class="line">            mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =</span><br><span class="line">            mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =</span><br><span class="line">                    res.getDimensionPixelSize(</span><br><span class="line">                            com.android.internal.R.dimen.navigation_bar_height_car_mode);</span><br><span class="line">            mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =</span><br><span class="line">            mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(</span><br><span class="line">                    com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Width of the navigation bar when presented vertically along one side</span></span><br><span class="line">            mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =</span><br><span class="line">            mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =</span><br><span class="line">            mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =</span><br><span class="line">            mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =</span><br><span class="line">                    res.getDimensionPixelSize(</span><br><span class="line">                            com.android.internal.R.dimen.navigation_bar_width_car_mode);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">getNavigationBarWidth</span><span class="params">(<span class="keyword">int</span> rotation, <span class="keyword">int</span> uiMode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (ALTERNATE_CAR_MODE_NAV_SIZE &amp;&amp; (uiMode &amp; UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) &#123;</span><br><span class="line">            <span class="keyword">return</span> mNavigationBarWidthForRotationInCarMode[rotation];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> mNavigationBarWidthForRotationDefault[rotation];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">getNavigationBarHeight</span><span class="params">(<span class="keyword">int</span> rotation, <span class="keyword">int</span> uiMode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (ALTERNATE_CAR_MODE_NAV_SIZE &amp;&amp; (uiMode &amp; UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) &#123;</span><br><span class="line">            <span class="keyword">return</span> mNavigationBarHeightForRotationInCarMode[rotation];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> mNavigationBarHeightForRotationDefault[rotation];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Preflight adding a window to the system.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * Currently enforces that three window types are singletons:</span></span><br><span class="line"><span class="comment">     * &lt;ul&gt;</span></span><br><span class="line"><span class="comment">     * &lt;li&gt;STATUS_BAR_TYPE&lt;/li&gt;</span></span><br><span class="line"><span class="comment">     * &lt;li&gt;KEYGUARD_TYPE&lt;/li&gt;</span></span><br><span class="line"><span class="comment">     * &lt;/ul&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> win The window to be added</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> attrs Information about the window to be added</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,</span></span><br><span class="line"><span class="comment">     * WindowManagerImpl.ADD_MULTIPLE_SINGLETON</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">prepareAddWindowLw</span><span class="params">(WindowState win, WindowManager.LayoutParams attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (attrs.type) &#123;</span><br><span class="line">            <span class="keyword">case</span> TYPE_STATUS_BAR:</span><br><span class="line">                mContext.enforceCallingOrSelfPermission(</span><br><span class="line">                        android.Manifest.permission.STATUS_BAR_SERVICE,</span><br><span class="line">                        <span class="string">"PhoneWindowManager"</span>);</span><br><span class="line">                <span class="keyword">if</span> (mStatusBar != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mStatusBar.isAlive()) &#123;</span><br><span class="line">                        <span class="keyword">return</span> WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                mStatusBar = win;</span><br><span class="line">                mStatusBarController.setWindow(win);</span><br><span class="line">                setKeyguardOccludedLw(mKeyguardOccluded, <span class="keyword">true</span> <span class="comment">/* force */</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> TYPE_NAVIGATION_BAR:</span><br><span class="line">                mContext.enforceCallingOrSelfPermission(</span><br><span class="line">                        android.Manifest.permission.STATUS_BAR_SERVICE,</span><br><span class="line">                        <span class="string">"PhoneWindowManager"</span>);</span><br><span class="line">                <span class="keyword">if</span> (mNavigationBar != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mNavigationBar.isAlive()) &#123;</span><br><span class="line">                        <span class="keyword">return</span> WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                mNavigationBar = win;</span><br><span class="line">                mNavigationBarController.setWindow(win);</span><br><span class="line">                mNavigationBarController.setOnBarVisibilityChangedListener(</span><br><span class="line">                        mNavBarVisibilityListener, <span class="keyword">true</span>);</span><br><span class="line">                <span class="keyword">if</span> (DEBUG_LAYOUT) Slog.i(TAG, <span class="string">"NAVIGATION BAR: "</span> + mNavigationBar);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> TYPE_NAVIGATION_BAR_PANEL:</span><br><span class="line">            <span class="keyword">case</span> TYPE_STATUS_BAR_PANEL:</span><br><span class="line">            <span class="keyword">case</span> TYPE_STATUS_BAR_SUB_PANEL:</span><br><span class="line">            <span class="keyword">case</span> TYPE_VOICE_INTERACTION_STARTING:</span><br><span class="line">                mContext.enforceCallingOrSelfPermission(</span><br><span class="line">                        android.Manifest.permission.STATUS_BAR_SERVICE,</span><br><span class="line">                        <span class="string">"PhoneWindowManager"</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ADD_OKAY;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/** &#123;<span class="doctag">@inheritDoc</span>&#125; */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">removeWindowLw</span><span class="params">(WindowState win)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mStatusBar == win) &#123;</span><br><span class="line">            mStatusBar = <span class="keyword">null</span>;</span><br><span class="line">            mStatusBarController.setWindow(<span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mNavigationBar == win) &#123;</span><br><span class="line">            mNavigationBar = <span class="keyword">null</span>;</span><br><span class="line">            mNavigationBarController.setWindow(<span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/** &#123;<span class="doctag">@inheritDoc</span>&#125; */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">selectAnimationLw</span><span class="params">(WindowState win, <span class="keyword">int</span> transit)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (PRINT_ANIM) Log.i(TAG, <span class="string">"selectAnimation in "</span> + win</span><br><span class="line">              + <span class="string">": transit="</span> + transit);</span><br><span class="line">        <span class="keyword">if</span> (win == mStatusBar) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">boolean</span> isKeyguard = (win.getAttrs().privateFlags &amp; PRIVATE_FLAG_KEYGUARD) != <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">boolean</span> expanded = win.getAttrs().height == MATCH_PARENT</span><br><span class="line">                    &amp;&amp; win.getAttrs().width == MATCH_PARENT;</span><br><span class="line">            <span class="keyword">if</span> (isKeyguard || expanded) &#123;</span><br><span class="line">                <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (transit == TRANSIT_EXIT</span><br><span class="line">                    || transit == TRANSIT_HIDE) &#123;</span><br><span class="line">                <span class="keyword">return</span> R.anim.dock_top_exit;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (transit == TRANSIT_ENTER</span><br><span class="line">                    || transit == TRANSIT_SHOW) &#123;</span><br><span class="line">                <span class="keyword">return</span> R.anim.dock_top_enter;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (win == mNavigationBar) &#123;</span><br><span class="line">            <span class="keyword">if</span> (win.getAttrs().windowAnimations != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">           ...</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/** &#123;<span class="doctag">@inheritDoc</span>&#125; */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">interceptKeyBeforeDispatching</span><span class="params">(WindowState win, KeyEvent event, <span class="keyword">int</span> policyFlags)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> keyguardOn = keyguardOn();</span><br><span class="line">        <span class="comment">/*final*/</span> <span class="keyword">int</span> keyCode = event.getKeyCode();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> repeatCount = event.getRepeatCount();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> metaState = event.getMetaState();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> flags = event.getFlags();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> down = event.getAction() == KeyEvent.ACTION_DOWN;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> canceled = event.isCanceled();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> longPress = (flags &amp; KeyEvent.FLAG_LONG_PRESS) != <span class="number">0</span>;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Runnable mClearHideNavigationFlag = <span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (mWindowManagerFuncs.getWindowManagerLock()) &#123;</span><br><span class="line">                <span class="comment">// Clear flags.</span></span><br><span class="line">                mForceClearedSystemUiFlags &amp;=</span><br><span class="line">                        ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;</span><br><span class="line">            &#125;</span><br><span class="line">            mWindowManagerFuncs.reevaluateStatusBarVisibility();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Input handler used while nav bar is hidden.  Captures any touch on the screen,</span></span><br><span class="line"><span class="comment">     * to determine when the nav bar should be shown and prevent applications from</span></span><br><span class="line"><span class="comment">     * receiving those touches.</span></span><br><span class="line"><span class="comment">     * 当状态栏和导航栏处于隐藏状态的时候，点击屏幕，会显示出状态栏和导航栏，显示时间大概1s</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">HideNavInputEventReceiver</span> <span class="keyword">extends</span> <span class="title">InputEventReceiver</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">HideNavInputEventReceiver</span><span class="params">(InputChannel inputChannel, Looper looper)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">super</span>(inputChannel, looper);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onInputEvent</span><span class="params">(InputEvent event, <span class="keyword">int</span> displayId)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">boolean</span> handled = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (event <span class="keyword">instanceof</span> MotionEvent</span><br><span class="line">                        &amp;&amp; (event.getSource() &amp; InputDevice.SOURCE_CLASS_POINTER) != <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">final</span> MotionEvent motionEvent = (MotionEvent)event;</span><br><span class="line">                    <span class="keyword">if</span> (motionEvent.getAction() == MotionEvent.ACTION_DOWN) &#123;</span><br><span class="line">                        <span class="comment">// When the user taps down, we re-show the nav bar.</span></span><br><span class="line">                        <span class="keyword">boolean</span> changed = <span class="keyword">false</span>;</span><br><span class="line">                        <span class="keyword">synchronized</span> (mWindowManagerFuncs.getWindowManagerLock()) &#123;</span><br><span class="line">                            <span class="keyword">if</span> (mInputConsumer == <span class="keyword">null</span>) &#123;</span><br><span class="line">                                <span class="keyword">return</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="comment">// Any user activity always causes us to show the</span></span><br><span class="line">                            <span class="comment">// navigation controls, if they had been hidden.</span></span><br><span class="line">                            <span class="comment">// We also clear the low profile and only content</span></span><br><span class="line">                            <span class="comment">// flags so that tapping on the screen will atomically</span></span><br><span class="line">                            <span class="comment">// restore all currently hidden screen decorations.</span></span><br><span class="line">                            <span class="keyword">int</span> newVal = mResettingSystemUiFlags |</span><br><span class="line">                                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |</span><br><span class="line">                                    View.SYSTEM_UI_FLAG_LOW_PROFILE |</span><br><span class="line">                                    View.SYSTEM_UI_FLAG_FULLSCREEN;</span><br><span class="line">                            <span class="keyword">if</span> (mResettingSystemUiFlags != newVal) &#123;</span><br><span class="line">                                mResettingSystemUiFlags = newVal;</span><br><span class="line">                                changed = <span class="keyword">true</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="comment">// We don't allow the system's nav bar to be hidden</span></span><br><span class="line">                            <span class="comment">// again for 1 second, to prevent applications from</span></span><br><span class="line">                            <span class="comment">// spamming us and keeping it from being shown.</span></span><br><span class="line">                            newVal = mForceClearedSystemUiFlags |</span><br><span class="line">                                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;</span><br><span class="line">                            <span class="keyword">if</span> (mForceClearedSystemUiFlags != newVal) &#123;</span><br><span class="line">                                mForceClearedSystemUiFlags = newVal;</span><br><span class="line">                                changed = <span class="keyword">true</span>;</span><br><span class="line">                                mHandler.postDelayed(mClearHideNavigationFlag, <span class="number">1000</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (changed) &#123;</span><br><span class="line">                            mWindowManagerFuncs.reevaluateStatusBarVisibility();</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                finishInputEvent(event, handled);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">adjustSystemUiVisibilityLw</span><span class="params">(<span class="keyword">int</span> visibility)</span> </span>&#123;</span><br><span class="line">        mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);</span><br><span class="line">        mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Reset any bits in mForceClearingStatusBarVisibility that</span></span><br><span class="line">        <span class="comment">// are now clear.</span></span><br><span class="line">        mResettingSystemUiFlags &amp;= visibility;</span><br><span class="line">        <span class="comment">// Clear any bits in the new visibility that are currently being</span></span><br><span class="line">        <span class="comment">// force cleared, before reporting it.</span></span><br><span class="line">        <span class="keyword">return</span> visibility &amp; ~mResettingSystemUiFlags</span><br><span class="line">                &amp; ~mForceClearedSystemUiFlags;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">layoutStatusBar</span><span class="params">(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, <span class="keyword">int</span> sysui,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> isKeyguardShowing)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// decide where the status bar goes ahead of time</span></span><br><span class="line">        <span class="keyword">if</span> (mStatusBar != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// apply any navigation bar insets</span></span><br><span class="line">            pf.left = df.left = of.left = mUnrestrictedScreenLeft;</span><br><span class="line">            pf.top = df.top = of.top = mUnrestrictedScreenTop;</span><br><span class="line">            pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;</span><br><span class="line">            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight</span><br><span class="line">                    + mUnrestrictedScreenTop;</span><br><span class="line">            vf.left = mStableLeft;</span><br><span class="line">            vf.top = mStableTop;</span><br><span class="line">            vf.right = mStableRight;</span><br><span class="line">            vf.bottom = mStableBottom;</span><br><span class="line"></span><br><span class="line">            mStatusBarLayer = mStatusBar.getSurfaceLayer();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Let the status bar determine its size.</span></span><br><span class="line">            mStatusBar.computeFrameLw(pf <span class="comment">/* parentFrame */</span>, df <span class="comment">/* displayFrame */</span>,</span><br><span class="line">                    vf <span class="comment">/* overlayFrame */</span>, vf <span class="comment">/* contentFrame */</span>, vf <span class="comment">/* visibleFrame */</span>,</span><br><span class="line">                    dcf <span class="comment">/* decorFrame */</span>, vf <span class="comment">/* stableFrame */</span>, vf <span class="comment">/* outsetFrame */</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// For layout, the status bar is always at the top with our fixed height.</span></span><br><span class="line">            mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">boolean</span> statusBarTransient = (sysui &amp; View.STATUS_BAR_TRANSIENT) != <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">boolean</span> statusBarTranslucent = (sysui</span><br><span class="line">                    &amp; (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span> (!isKeyguardShowing) &#123;</span><br><span class="line">                statusBarTranslucent &amp;= areTranslucentBarsAllowed();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// If the status bar is hidden, we don't want to cause</span></span><br><span class="line">            <span class="comment">// windows behind it to scroll.</span></span><br><span class="line">            <span class="keyword">if</span> (mStatusBar.isVisibleLw() &amp;&amp; !statusBarTransient) &#123;</span><br><span class="line">                <span class="comment">// Status bar may go away, so the screen area it occupies</span></span><br><span class="line">                <span class="comment">// is available to apps but just covering them when the</span></span><br><span class="line">                <span class="comment">// status bar is visible.</span></span><br><span class="line">                mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;</span><br><span class="line"></span><br><span class="line">                mContentTop = mVoiceContentTop = mCurTop = mDockTop;</span><br><span class="line">                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;</span><br><span class="line">                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;</span><br><span class="line">                mContentRight = mVoiceContentRight = mCurRight = mDockRight;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (DEBUG_LAYOUT) Slog.v(TAG, <span class="string">"Status bar: "</span> +</span><br><span class="line">                        String.format(</span><br><span class="line">                                <span class="string">"dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]"</span>,</span><br><span class="line">                                mDockLeft, mDockTop, mDockRight, mDockBottom,</span><br><span class="line">                                mContentLeft, mContentTop, mContentRight, mContentBottom,</span><br><span class="line">                                mCurLeft, mCurTop, mCurRight, mCurBottom));</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mStatusBar.isVisibleLw() &amp;&amp; !mStatusBar.isAnimatingLw()</span><br><span class="line">                    &amp;&amp; !statusBarTransient &amp;&amp; !statusBarTranslucent</span><br><span class="line">                    &amp;&amp; !mStatusBarController.wasRecentlyTranslucent()) &#123;</span><br><span class="line">                <span class="comment">// If the opaque status bar is currently requested to be visible,</span></span><br><span class="line">                <span class="comment">// and not in the process of animating on or off, then</span></span><br><span class="line">                <span class="comment">// we can tell the app that it is covered by it.</span></span><br><span class="line">                mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mStatusBarController.checkHiddenLw()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">layoutNavigationBar</span><span class="params">(<span class="keyword">int</span> displayWidth, <span class="keyword">int</span> displayHeight, <span class="keyword">int</span> displayRotation,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> uiMode, <span class="keyword">int</span> overscanLeft, <span class="keyword">int</span> overscanRight, <span class="keyword">int</span> overscanBottom, Rect dcf,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> navVisible, <span class="keyword">boolean</span> navTranslucent, <span class="keyword">boolean</span> navAllowedHidden,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> statusBarExpandedNotKeyguard)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mNavigationBar != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">boolean</span> transientNavBarShowing = mNavigationBarController.isTransientShowing();</span><br><span class="line">            <span class="comment">// Force the navigation bar to its appropriate place and</span></span><br><span class="line">            <span class="comment">// size.  We need to do this directly, instead of relying on</span></span><br><span class="line">            <span class="comment">// it to bubble up from the nav bar, because this needs to</span></span><br><span class="line">            <span class="comment">// change atomically with screen rotations.</span></span><br><span class="line">            mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,</span><br><span class="line">                    displayRotation);</span><br><span class="line">            <span class="keyword">if</span> (mNavigationBarPosition == NAV_BAR_BOTTOM) &#123;</span><br><span class="line">                <span class="comment">// It's a system nav bar or a portrait screen; nav bar goes on bottom.</span></span><br><span class="line">                <span class="keyword">int</span> top = displayHeight - overscanBottom</span><br><span class="line">                        - getNavigationBarHeight(displayRotation, uiMode);</span><br><span class="line">                mTmpNavigationFrame.set(<span class="number">0</span>, top, displayWidth, displayHeight - overscanBottom);</span><br><span class="line">                mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;</span><br><span class="line">                <span class="keyword">if</span> (transientNavBarShowing) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (navVisible) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                    mDockBottom = mTmpNavigationFrame.top;</span><br><span class="line">                    mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;</span><br><span class="line">                    mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// We currently want to hide the navigation UI - unless we expanded the status</span></span><br><span class="line">                    <span class="comment">// bar.</span></span><br><span class="line">                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (navVisible &amp;&amp; !navTranslucent &amp;&amp; !navAllowedHidden</span><br><span class="line">                        &amp;&amp; !mNavigationBar.isAnimatingLw()</span><br><span class="line">                        &amp;&amp; !mNavigationBarController.wasRecentlyTranslucent()) &#123;</span><br><span class="line">                    <span class="comment">// If the opaque nav bar is currently requested to be visible,</span></span><br><span class="line">                    <span class="comment">// and not in the process of animating on or off, then</span></span><br><span class="line">                    <span class="comment">// we can tell the app that it is covered by it.</span></span><br><span class="line">                    mSystemBottom = mTmpNavigationFrame.top;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mNavigationBarPosition == NAV_BAR_RIGHT) &#123;</span><br><span class="line">                <span class="comment">// Landscape screen; nav bar goes to the right.</span></span><br><span class="line">                <span class="keyword">int</span> left = displayWidth - overscanRight</span><br><span class="line">                        - getNavigationBarWidth(displayRotation, uiMode);</span><br><span class="line">                mTmpNavigationFrame.set(left, <span class="number">0</span>, displayWidth - overscanRight, displayHeight);</span><br><span class="line">                mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;</span><br><span class="line">                <span class="keyword">if</span> (transientNavBarShowing) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (navVisible) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                    mDockRight = mTmpNavigationFrame.left;</span><br><span class="line">                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;</span><br><span class="line">                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// We currently want to hide the navigation UI - unless we expanded the status</span></span><br><span class="line">                    <span class="comment">// bar.</span></span><br><span class="line">                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (navVisible &amp;&amp; !navTranslucent &amp;&amp; !navAllowedHidden</span><br><span class="line">                        &amp;&amp; !mNavigationBar.isAnimatingLw()</span><br><span class="line">                        &amp;&amp; !mNavigationBarController.wasRecentlyTranslucent()) &#123;</span><br><span class="line">                    <span class="comment">// If the nav bar is currently requested to be visible,</span></span><br><span class="line">                    <span class="comment">// and not in the process of animating on or off, then</span></span><br><span class="line">                    <span class="comment">// we can tell the app that it is covered by it.</span></span><br><span class="line">                    mSystemRight = mTmpNavigationFrame.left;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mNavigationBarPosition == NAV_BAR_LEFT) &#123;</span><br><span class="line">                <span class="comment">// Seascape screen; nav bar goes to the left.</span></span><br><span class="line">                <span class="keyword">int</span> right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);</span><br><span class="line">                mTmpNavigationFrame.set(overscanLeft, <span class="number">0</span>, right, displayHeight);</span><br><span class="line">                mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;</span><br><span class="line">                <span class="keyword">if</span> (transientNavBarShowing) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (navVisible) &#123;</span><br><span class="line">                    mNavigationBarController.setBarShowingLw(<span class="keyword">true</span>);</span><br><span class="line">                    mDockLeft = mTmpNavigationFrame.right;</span><br><span class="line">                    <span class="comment">// <span class="doctag">TODO:</span> not so sure about those:</span></span><br><span class="line">                    mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;</span><br><span class="line">                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;</span><br><span class="line">                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// We currently want to hide the navigation UI - unless we expanded the status</span></span><br><span class="line">                    <span class="comment">// bar.</span></span><br><span class="line">                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (navVisible &amp;&amp; !navTranslucent &amp;&amp; !navAllowedHidden</span><br><span class="line">                        &amp;&amp; !mNavigationBar.isAnimatingLw()</span><br><span class="line">                        &amp;&amp; !mNavigationBarController.wasRecentlyTranslucent()) &#123;</span><br><span class="line">                    <span class="comment">// If the nav bar is currently requested to be visible,</span></span><br><span class="line">                    <span class="comment">// and not in the process of animating on or off, then</span></span><br><span class="line">                    <span class="comment">// we can tell the app that it is covered by it.</span></span><br><span class="line">                    mSystemLeft = mTmpNavigationFrame.right;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// Make sure the content and current rectangles are updated to</span></span><br><span class="line">            <span class="comment">// account for the restrictions from the navigation bar.</span></span><br><span class="line">            mContentTop = mVoiceContentTop = mCurTop = mDockTop;</span><br><span class="line">            mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;</span><br><span class="line">            mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;</span><br><span class="line">            mContentRight = mVoiceContentRight = mCurRight = mDockRight;</span><br><span class="line">            mStatusBarLayer = mNavigationBar.getSurfaceLayer();</span><br><span class="line">            <span class="comment">// And compute the final frame.</span></span><br><span class="line">            mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,</span><br><span class="line">                    mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,</span><br><span class="line">                    mTmpNavigationFrame, mTmpNavigationFrame);</span><br><span class="line">            <span class="keyword">if</span> (DEBUG_LAYOUT) Slog.i(TAG, <span class="string">"mNavigationBar frame: "</span> + mTmpNavigationFrame);</span><br><span class="line">            <span class="keyword">if</span> (mNavigationBarController.checkHiddenLw()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><h1 id="快捷开关"><a href="#快捷开关" class="headerlink" title="快捷开关"></a>快捷开关</h1><h1 id="下拉通知栏"><a href="#下拉通知栏" class="headerlink" title="下拉通知栏"></a>下拉通知栏</h1><h1 id="截图模块"><a href="#截图模块" class="headerlink" title="截图模块"></a>截图模块</h1><h1 id="防误触模块"><a href="#防误触模块" class="headerlink" title="防误触模块"></a>防误触模块</h1><h1 id="亮度调节"><a href="#亮度调节" class="headerlink" title="亮度调节"></a>亮度调节</h1><h1 id="PIP-画中画"><a href="#PIP-画中画" class="headerlink" title="PIP 画中画"></a>PIP 画中画</h1><h1 id="双屏异显"><a href="#双屏异显" class="headerlink" title="双屏异显"></a>双屏异显</h1><h1 id="Recent模块"><a href="#Recent模块" class="headerlink" title="Recent模块"></a>Recent模块</h1><p>在AndroidManifest.xml里面，我们注意到SystemUISecondaryUserService，对于每一个用户都有一个SystemUI进程，所以在用户切换的时候，需要用SystemUISecondaryUserService来确保新的用户进程创建.<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Recents depends on every user having their own SystemUI process, so on user switch,</span></span><br><span class="line"><span class="comment">     ensure that the process is created by starting this service.</span></span><br><span class="line"><span class="comment">     --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">service</span> <span class="attr">android:name</span>=<span class="string">"SystemUISecondaryUserService"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:exported</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:permission</span>=<span class="string">"com.android.systemui.permission.SELF"</span> /&gt;</span></span><br></pre></td></tr></table></figure></p><p>RecentsActivity.java的onCreate函数开始<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RecentsActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> <span class="keyword">implements</span> <span class="title">ViewTreeObserver</span>.<span class="title">OnPreDrawListener</span>,</span></span><br><span class="line"><span class="class">        <span class="title">ColorExtractor</span>.<span class="title">OnColorsChangedListener</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Called with the activity is first created. */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        mFinishedOnStartup = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// In the case that the activity starts up before the Recents component has initialized</span></span><br><span class="line">        <span class="comment">// (usually when debugging/pushing the SysUI apk), just finish this activity.</span></span><br><span class="line">        SystemServicesProxy ssp = Recents.getSystemServices();</span><br><span class="line">        <span class="keyword">if</span> (ssp == <span class="keyword">null</span>) &#123;</span><br><span class="line">            mFinishedOnStartup = <span class="keyword">true</span>;</span><br><span class="line">            finish();</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></p><h1 id="SystemUI-新功能"><a href="#SystemUI-新功能" class="headerlink" title="SystemUI 新功能"></a>SystemUI 新功能</h1><h2 id="隐藏导航栏"><a href="#隐藏导航栏" class="headerlink" title="隐藏导航栏"></a>隐藏导航栏</h2><h2 id="悬浮按钮"><a href="#悬浮按钮" class="headerlink" title="悬浮按钮"></a>悬浮按钮</h2><h2 id="单手模式-小屏模式"><a href="#单手模式-小屏模式" class="headerlink" title="单手模式(小屏模式)"></a>单手模式(小屏模式)</h2><h2 id="单手键盘"><a href="#单手键盘" class="headerlink" title="单手键盘"></a>单手键盘</h2><h2 id="口袋模式-防误触模式"><a href="#口袋模式-防误触模式" class="headerlink" title="口袋模式(防误触模式)"></a>口袋模式(防误触模式)</h2><h2 id="凹口设计"><a href="#凹口设计" class="headerlink" title="凹口设计"></a>凹口设计</h2><h2 id="灭屏动画"><a href="#灭屏动画" class="headerlink" title="灭屏动画"></a>灭屏动画</h2><h2 id="翻转静音"><a href="#翻转静音" class="headerlink" title="翻转静音"></a>翻转静音</h2><p>1.来电静音<br>2.计时器和闹钟静音</p><h2 id="双击唤醒"><a href="#双击唤醒" class="headerlink" title="双击唤醒"></a>双击唤醒</h2><h2 id="指关节截屏"><a href="#指关节截屏" class="headerlink" title="指关节截屏"></a>指关节截屏</h2><h2 id="字母手势"><a href="#字母手势" class="headerlink" title="字母手势"></a>字母手势</h2><h2 id="面部解锁"><a href="#面部解锁" class="headerlink" title="面部解锁"></a>面部解锁</h2><h2 id="画中画"><a href="#画中画" class="headerlink" title="画中画"></a>画中画</h2><h2 id="异屏双显"><a href="#异屏双显" class="headerlink" title="异屏双显"></a>异屏双显</h2><h2 id="沉浸模式"><a href="#沉浸模式" class="headerlink" title="沉浸模式"></a>沉浸模式</h2><p>##SB和NB颜色</p><h2 id="通知栏和状态栏功能"><a href="#通知栏和状态栏功能" class="headerlink" title="通知栏和状态栏功能"></a>通知栏和状态栏功能</h2><h3 id="通知"><a href="#通知" class="headerlink" title="通知"></a>通知</h3><p>通知：允许通知.通知方式：状态栏上显示、在屏幕顶部悬浮显示、在锁屏上显示.</p><h3 id="通知栏："><a href="#通知栏：" class="headerlink" title="通知栏："></a>通知栏：</h3><p>   通知栏下拉规则:1)智能判断，有通知时，下拉显示通知，无通知时，下拉显示开关<br>   2)依据下拉位置来判断:左边下拉显示通知页，右边下拉显示开关页<br>   在通知栏显示流量信息.</p><h3 id="状态栏："><a href="#状态栏：" class="headerlink" title="状态栏："></a>状态栏：</h3><pre><code>显示运营商名称有通知时显示图标显示实时网速显示电量百分比，电量百分比显示方式</code></pre><h1 id="Demo模式命令调试SystemUI"><a href="#Demo模式命令调试SystemUI" class="headerlink" title="Demo模式命令调试SystemUI"></a>Demo模式命令调试SystemUI</h1><p>在SystemUI目录有一个Readme.md文件，在8.1.0版本中内容如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">* [Demo Mode](/packages/SystemUI/docs/demo_mode.md)</span><br></pre></td></tr></table></figure></p><p>我们到此目录查看demo_mode.md文件<br>如下：<br>Demo Mode for the Android System UI<br><em>Demo mode for the status bar allows you to force the status bar into a fixed state, useful for taking screenshots with a consistent status bar state, or testing different status icon permutations. Demo mode is available in recent versions of Android.</em></p><h2 id="Enabling-demo-mode"><a href="#Enabling-demo-mode" class="headerlink" title="Enabling demo mode"></a>Enabling demo mode</h2><p>Demo mode is protected behind a system setting. To enable it for a device, run:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell settings put global sysui_demo_allowed 1</span><br></pre></td></tr></table></figure><h2 id="Protocol"><a href="#Protocol" class="headerlink" title="Protocol"></a>Protocol</h2><p>The protocol is based on broadcast intents, and thus can be driven via the command line (adb shell am broadcast) or an app (Context.sendBroadcast).</p><h3 id="Broadcast-action"><a href="#Broadcast-action" class="headerlink" title="Broadcast action"></a>Broadcast action</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">com.android.systemui.demo</span><br></pre></td></tr></table></figure><h3 id="Commands"><a href="#Commands" class="headerlink" title="Commands"></a>Commands</h3><p>Commands and subcommands (below) are sent as string extras in the broadcast<br>intent.<br><br><br>Commands are sent as string extras with key <em>command</em> (required). Possible values are:</p><table><thead><tr><th>Command</th><th>Subcommand</th><th>Argument</th><th>Description</th></tr></thead><tbody><tr><td><em>enter</em></td><td></td><td></td><td>Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)</td></tr><tr><td><em>exit</em></td><td></td><td></td><td>Exits demo mode, bars back to their system-driven state</td></tr><tr><td><em>battery</em></td><td></td><td></td><td>Control the battery display</td></tr><tr><td></td><td><em>level</em></td><td></td><td>Sets the battery level (0 - 100)</td></tr><tr><td></td><td><em>plugged</em></td><td></td><td>Sets charging state (<em>true</em>, <em>false</em>)</td></tr><tr><td></td><td><em>powersave</em></td><td></td><td>Sets power save mode (<em>true</em>, <em>anything else</em>)</td></tr><tr><td><em>network</em></td><td></td><td></td><td>Control the RSSI display</td></tr><tr><td></td><td><em>airplane</em></td><td></td><td><em>show</em> to show icon, any other value to hide</td></tr><tr><td></td><td><em>fully</em></td><td></td><td>Sets MCS state to fully connected (<em>true</em>, <em>false</em>)</td></tr><tr><td></td><td><em>wifi</em></td><td></td><td><em>show</em> to show icon, any other value to hide</td></tr><tr><td></td><td></td><td><em>level</em></td><td>Sets wifi level (null or 0-4)</td></tr><tr><td></td><td><em>mobile</em></td><td></td><td><em>show</em> to show icon, any other value to hide</td></tr><tr><td></td><td></td><td><em>datatype</em></td><td>Values: <em>1x</em>, <em>3g</em>, <em>4g</em>, <em>e</em>, <em>g</em>, <em>h</em>, <em>lte</em>, <em>roam</em>, any other value to hide</td></tr><tr><td></td><td></td><td><em>level</em></td><td>Sets mobile signal strength level (null or 0-4)</td></tr><tr><td></td><td><em>carriernetworkchange</em></td><td></td><td>Sets mobile signal icon to carrier network change UX when disconnected (<em>show</em> to show icon, any other value to hide)</td></tr><tr><td></td><td><em>sims</em></td><td></td><td>Sets the number of sims (1-8)</td></tr><tr><td></td><td><em>nosim</em></td><td></td><td><em>show</em> to show icon, any other value to hide</td></tr><tr><td><em>bars</em></td><td></td><td></td><td>Control the visual style of the bars (opaque, translucent, etc)</td></tr><tr><td></td><td><em>mode</em></td><td></td><td>Sets the bars visual style (opaque, translucent, semi-transparent)</td></tr><tr><td><em>status</em></td><td></td><td></td><td>Control the system status icons</td></tr><tr><td></td><td><em>volume</em></td><td></td><td>Sets the icon in the volume slot (<em>silent</em>, <em>vibrate</em>, any other value to hide)</td></tr><tr><td></td><td><em>bluetooth</em></td><td></td><td>Sets the icon in the bluetooth slot (<em>connected</em>, <em>disconnected</em>, any other value to hide)</td></tr><tr><td></td><td><em>location</em></td><td></td><td>Sets the icon in the location slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>alarm</em></td><td></td><td>Sets the icon in the alarm_clock slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>sync</em></td><td></td><td>Sets the icon in the sync_active slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>tty</em></td><td></td><td>Sets the icon in the tty slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>eri</em></td><td></td><td>Sets the icon in the cdma_eri slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>mute</em></td><td></td><td>Sets the icon in the mute slot (<em>show</em>, any other value to hide)</td></tr><tr><td></td><td><em>speakerphone</em></td><td></td><td>Sets the icon in the speakerphone slot (<em>show</em>, any other value to hide)</td></tr><tr><td><em>notifications</em></td><td></td><td></td><td>Control the notification icons</td></tr><tr><td></td><td><em>visible</em></td><td></td><td><em>false</em> to hide the notification icons, any other value to show</td></tr><tr><td><em>clock</em></td><td></td><td></td><td>Control the clock display</td></tr><tr><td></td><td><em>millis</em></td><td></td><td>Sets the time in millis</td></tr><tr><td></td><td><em>hhmm</em></td><td></td><td>Sets the time in hh:mm</td></tr></tbody></table><h2 id="Examples"><a href="#Examples" class="headerlink" title="Examples"></a>Examples</h2><p>Enter demo mode</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command enter</span><br></pre></td></tr></table></figure><p>Exit demo mode</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command exit</span><br></pre></td></tr></table></figure><p>Set the clock to 12:31</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm</span><br><span class="line">1231</span><br></pre></td></tr></table></figure><p>Set the wifi level to max</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi</span><br><span class="line">show -e level 4</span><br></pre></td></tr></table></figure><p>Show the silent volume icon</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command status -e volume</span><br><span class="line">silent</span><br></pre></td></tr></table></figure><p>Empty battery, and not charging (red exclamation point)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command battery -e level</span><br><span class="line">0 -e plugged false</span><br></pre></td></tr></table></figure><p>Hide the notification icons</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command notifications -e</span><br><span class="line">visible false</span><br></pre></td></tr></table></figure><p>Exit demo mode</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell am broadcast -a com.android.systemui.demo -e command exit</span><br></pre></td></tr></table></figure><h2 id="Example-demo-controller-app-in-AOSP"><a href="#Example-demo-controller-app-in-AOSP" class="headerlink" title="Example demo controller app in AOSP"></a>Example demo controller app in AOSP</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">frameworks/base/tests/SystemUIDemoModeController</span><br></pre></td></tr></table></figure><h2 id="Example-script-for-screenshotting-purposes"><a href="#Example-script-for-screenshotting-purposes" class="headerlink" title="Example script (for screenshotting purposes)"></a>Example script (for screenshotting purposes)</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">CMD=<span class="variable">$1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$ADB</span> == <span class="string">""</span> ]]; <span class="keyword">then</span></span><br><span class="line">  ADB=adb</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$CMD</span> != <span class="string">"on"</span> &amp;&amp; <span class="variable">$CMD</span> != <span class="string">"off"</span> ]]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">"Usage: <span class="variable">$0</span> [on|off] [hhmm]"</span> &gt;&amp;2</span><br><span class="line">  <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="string">"<span class="variable">$2</span>"</span> != <span class="string">""</span> ]]; <span class="keyword">then</span></span><br><span class="line">  HHMM=<span class="string">"<span class="variable">$2</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$ADB</span> root || <span class="built_in">exit</span></span><br><span class="line"><span class="variable">$ADB</span> <span class="built_in">wait</span>-for-devices</span><br><span class="line"><span class="variable">$ADB</span> shell settings put global sysui_demo_allowed 1</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$CMD</span> == <span class="string">"on"</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> enter || <span class="built_in">exit</span></span><br><span class="line">  <span class="keyword">if</span> [[ <span class="string">"<span class="variable">$HHMM</span>"</span> != <span class="string">""</span> ]]; <span class="keyword">then</span></span><br><span class="line">    <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> clock -e</span><br><span class="line">hhmm <span class="variable">$&#123;HHMM&#125;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> battery -e</span><br><span class="line">plugged <span class="literal">false</span></span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> battery -e</span><br><span class="line">level 100</span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> network -e</span><br><span class="line">wifi show -e level 4</span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> network -e</span><br><span class="line">mobile show -e datatype none -e level 4</span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> notifications</span><br><span class="line">-e visible <span class="literal">false</span></span><br><span class="line"><span class="keyword">elif</span> [ <span class="variable">$CMD</span> == <span class="string">"off"</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="variable">$ADB</span> shell am broadcast -a com.android.systemui.demo -e <span class="built_in">command</span> <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><h1 id="导航栏和状态栏的显示与隐藏"><a href="#导航栏和状态栏的显示与隐藏" class="headerlink" title="导航栏和状态栏的显示与隐藏"></a>导航栏和状态栏的显示与隐藏</h1><p>POLICY_CONTROL实现<br>隐藏虚拟键及顶部状态栏：<br>adb shell settings put global policy_control immersive.full=<em><br>隐藏顶部状态栏（底部虚拟键会显示）：<br>adb shell settings put global policy_control immersive.status=</em><br>隐藏虚拟键（顶部状态栏会显示）：<br>adb shell settings put global policy_control immersive.navigation=*<br>恢复原来的设置：<br>adb shell settings put global policy_control null</p><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h1><p>Doze模式<br><a href="https://blog.csdn.net/u013278940/article/details/50498304" target="_blank" rel="noopener">Android5.1 系统之省电模式探索一启动流程</a><br><a href="https://blog.csdn.net/innost/article/details/47660591" target="_blank" rel="noopener">《深入理解Android 卷III》第七章 深入理解SystemUI</a></p><p>NavigationBar相关<br><a href="https://blog.csdn.net/span76/article/details/48441971" target="_blank" rel="noopener">Android SystemUI中HOME key的处理</a><br><a href="https://blog.csdn.net/frakie_kwok/article/details/78730477" target="_blank" rel="noopener">去除首次进入沉浸模式气泡提示</a><br><a href="hrome-extension://lecdifefmmfjnjjinhaennhdlmcaeeeb/main.html" target="_blank" rel="noopener">Android 修改横屏角度为顺时针270度</a></p><p><a href="https://jingyan.baidu.com/article/76a7e409071797fc3b6e15bb.html" target="_blank" rel="noopener">SystemUI 调谐器 TunerService的打开方式</a></p><p><a href="https://www.jianshu.com/p/ad040aab0e66" target="_blank" rel="noopener">Android官方架构组件Navigation：大巧不工的Fragment管理框架</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;还未完成，待继续补充.&lt;/p&gt;
&lt;h1 id=&quot;SystemUI概述&quot;&gt;&lt;a href=&quot;#SystemUI概述&quot; class=&quot;headerlink&quot; title=&quot;SystemUI概述&quot;&gt;&lt;/a&gt;SystemUI概述&lt;/h1&gt;&lt;p&gt;Android8.1.0 System
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="SystemUI" scheme="http://yoursite.com/categories/Android/SystemUI/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="SystemUI" scheme="http://yoursite.com/tags/SystemUI/"/>
    
  </entry>
  
  <entry>
    <title>Android中WindowManagerService解析</title>
    <link href="http://yoursite.com/Android-WindowManagerService-Analysis/"/>
    <id>http://yoursite.com/Android-WindowManagerService-Analysis/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-09-04T02:33:13.213Z</updated>
    
    <content type="html"><![CDATA[<h1 id="WindowManagerService-概述"><a href="#WindowManagerService-概述" class="headerlink" title="WindowManagerService 概述"></a>WindowManagerService 概述</h1><p><img src="/Android-WindowManagerService-Analysis/WMS-01.png" alt="&quot;对象与实体之间的关系&quot;"></p><h1 id="关键文件分析"><a href="#关键文件分析" class="headerlink" title="关键文件分析"></a>关键文件分析</h1><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://blog.csdn.net/luoshengyang/article/details/8923485" target="_blank" rel="noopener">那两年炼就的Android内功修养</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8462738" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService的简要介绍和学习计划</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8479101" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8498908" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService对窗口的组织方式分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8526644" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService对输入法窗口（Input Method Window）的管理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8550820" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService对壁纸窗口（Wallpaper Window）的管理分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8570428" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8577789" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService显示Activity组件的启动窗口（Starting Window）的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8596449" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService切换Activity窗口（App Transition）的过程分析</a><br><a href="https://blog.csdn.net/luoshengyang/article/details/8611754" target="_blank" rel="noopener">Android窗口管理服务WindowManagerService显示窗口动画的原理分析</a></p><p><a href="https://www.cnblogs.com/wzjhoutai/p/6873790.html" target="_blank" rel="noopener">第4章  深入理解WindowManagerService（节选）</a><br><a href="https://www.jianshu.com/p/f3ec3f6e0f6b" target="_blank" rel="noopener">Android窗口系统第一篇—Window的类型与Z-Order确定</a> 主要讲窗口类型、坐标系统、Z-Order,Z-Order确定跟窗口类型有关系<br><a href="https://www.jianshu.com/p/ba53cf8694f1" target="_blank" rel="noopener">Android窗口系统第二篇—Window的添加过程</a><br><a href="https://blog.csdn.net/u013263323/article/details/78482141" target="_blank" rel="noopener">Android窗口系统第三篇—WindowManagerService中窗口的组织方式</a></p><p><a href="https://blog.csdn.net/yhaolpz/article/details/68936932" target="_blank" rel="noopener">Android 带你彻底理解 Window 和 WindowManager</a><br><a href="https://blog.csdn.net/itachi85/article/details/77888668" target="_blank" rel="noopener">Android解析WindowManager（一）WindowManager体系</a><br><a href="https://blog.csdn.net/itachi85/article/details/77939884" target="_blank" rel="noopener">Android解析WindowManager（二）Window的属性</a><br><a href="https://blog.csdn.net/sted_zxz/article/details/73277645" target="_blank" rel="noopener">Window和WindowManager(Android开发艺术探索学习笔记)</a><br><a href="https://blog.csdn.net/WHB20081815/article/details/62419059" target="_blank" rel="noopener">Android快速理解Activity、View及Window&amp;WindowManager之间关系</a></p><p><a href="https://blog.csdn.net/qq379454816/article/details/49736231" target="_blank" rel="noopener">Android之WindowManager实现悬浮按钮</a><br><a href="https://blog.csdn.net/qq_17250009/article/details/52908791" target="_blank" rel="noopener">Android 使用WindowManager实现悬浮窗及源码解析</a></p><p><a href="http://www.cnblogs.com/samchen2009/p/3364327.html" target="_blank" rel="noopener">图解Android - Android GUI 系统 (1) - 概论</a> 需要重点研究一下GUI</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;WindowManagerService-概述&quot;&gt;&lt;a href=&quot;#WindowManagerService-概述&quot; class=&quot;headerlink&quot; title=&quot;WindowManagerService 概述&quot;&gt;&lt;/a&gt;WindowManagerServ
      
    
    </summary>
    
      <category term="Android" scheme="http://yoursite.com/categories/Android/"/>
    
      <category term="WindowManagerService" scheme="http://yoursite.com/categories/Android/WindowManagerService/"/>
    
    
      <category term="Android" scheme="http://yoursite.com/tags/Android/"/>
    
      <category term="WindowManagerService" scheme="http://yoursite.com/tags/WindowManagerService/"/>
    
      <category term="WMS" scheme="http://yoursite.com/tags/WMS/"/>
    
  </entry>
  
  <entry>
    <title>读书笔记</title>
    <link href="http://yoursite.com/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    <id>http://yoursite.com/读书笔记/</id>
    <published>2014-12-31T16:00:00.000Z</published>
    <updated>2018-08-07T06:38:26.131Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>本文目的是记录阅读一些书籍过程中的知识点</p><h1 id="JVM-源码分析"><a href="#JVM-源码分析" class="headerlink" title="JVM 源码分析"></a>JVM 源码分析</h1><p> 下载JVM源码 <a href="https://download.java.net/openjdk/jdk7/" target="_blank" rel="noopener">https://download.java.net/openjdk/jdk7/</a><br> openjdk-7-ea-src-b31-17_jul_2008.zip中有hotspot目录,里面是vm的代码</p><h1 id="《剑指Offer-名企面试官精讲典型编程题》"><a href="#《剑指Offer-名企面试官精讲典型编程题》" class="headerlink" title="《剑指Offer:名企面试官精讲典型编程题》"></a>《剑指Offer:名企面试官精讲典型编程题》</h1><p>   2012年电子工业出版社,何海涛</p><p><a href="https://blog.csdn.net/Together_CZ/article/details/74906427" target="_blank" rel="noopener">剑指Offer学习</a><br><a href="https://blog.csdn.net/panda_AJ/article/details/69420293" target="_blank" rel="noopener">剑指offer题目及答案</a></p><p>#《Android应用安全防护和逆向分析》—姜维</p><ol><li>Android中锁屏密码加密算法分析<br>以Android 5.1版本，分析了密码算法 和手势密码算法</li><li>Android中NDK开发<br>写了简单的实例，实现Java调用C，C调用Java</li><li>Android 开发和逆向常用命令<br>介绍了一些常用的adb 命令</li><li>so文件格式解析<br>so文件是Android中C/C++模块编译出来的库，有静态库和动态库两种。Android中的so文件就是ELF文件。<br>文中介绍了阅读ELF文件的工具readelf的常用命令。</li><li><p>AndroidManifest.xml文件格式解析</p></li><li><p>resource.arsc文件格式解析<br>Resource.arsc文件对应的数据结构 frameworks/base/include/androidfw/ResourceTypes.h</p></li><li><p>dex 文件格式解析</p><p>防护篇</p></li><li><p>Android 应用安全防护的基本策略<br>1) 混淆机制.混淆不仅是为了安全防护，也是为了减小应用安装包的大小。混淆机制有两种:<br> a)代码混淆：混淆后的代码类名和方法名都变了<br> b)资源混淆： 微信已经开源资源混淆 <a href="http://github.com/shwenzhang/AndResGuard" target="_blank" rel="noopener">http://github.com/shwenzhang/AndResGuard</a><br>2) 签名保护<br>3）手动注册native方法<br>4）反调试监测</p></li><li>Android中常用权限分析</li></ol><p><a href="https://github.com/Juude/droidReverse" target="_blank" rel="noopener">逆向工程工具集</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;本文目的是记录阅读一些书籍过程中的知识点&lt;/p&gt;
&lt;h1 id=&quot;JVM-源码分析&quot;&gt;&lt;a href=&quot;#JVM-源码分析&quot; class=&quot;
      
    
    </summary>
    
      <category term="Hexo" scheme="http://yoursite.com/categories/Hexo/"/>
    
    
      <category term="逆向分析" scheme="http://yoursite.com/tags/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/"/>
    
      <category term="安全" scheme="http://yoursite.com/tags/%E5%AE%89%E5%85%A8/"/>
    
  </entry>
  
</feed>
